Cross-Language Integration

Prior to .NET, interoperating with code written in other languages was challenging. There were pretty much two options for reusing functionality developed in other languages: COM interfaces or DLLs with exported C functions.

Visual Basic is built on top of the CLR. As such it interoperates with code written in other .NET languages. It's even able to derive from a class written in another language. To support this type of functionality, the CLR relies on a common way of representing types, as well as rich metadata that can describe these types.

The Common Type System

Each programming language seems to bring its own island of data types with it. To help resolve this problem, C, an operating-system-level implementation language, became the lowest common denominator for interfacing between programs written in multiple languages. An exported function written in C that exposes simple C data types can be consumed by a variety of other programming languages. In fact, the Windows API is exposed for .NET developers as a set of C functions. Note this changes for those building Metro applications, but this does not change the value of the common type system (CTS) across .NET.

Unfortunately, to access a C interface, you must explicitly map C data types to a language's native data types. This is not only cumbersome, but also error prone due to the complexity. Accidentally mapping a variable declared as Long to lpBuffer wouldn't generate any compilation errors, but calling the function at runtime would result in difficult-to-diagnose, intermittent access violations at runtime.

The .NET CTS provides a set of common data types for use across all programming languages. It provides every language running on top of the .NET platform with a base set of types, as well as mechanisms for extending those types. These types are derived from a common System.Object class definition.

Because every type supported by the CTS is derived from System.Object, every type supports a common set of methods, as shown in Table 2.2.

Table 2.2 Common Type Methods

Method Description
Boolean Equals(Object) Used to test equality with another object. Reference types should return True if the Object parameter references the same object. Value types should return True if the Object parameter has the same value.
Int32 GetHashCode() Generates a number corresponding to the value of an object. If two objects of the same type are equal, then they must return the same hash code.
Type GetType() Gets a Type object that can be used to access metadata associated with the type. It also serves as a starting point for navigating the object hierarchy exposed by the Reflection API (discussed shortly).
String ToString() The default implementation returns the fully qualified name of the object's class. This method is often overridden to output data that is more meaningful to the type. For example, all base types return their value as a string.

Metadata

Metadata is the information that enables components to be self-describing. It describes many aspects of .NET components including classes, methods, and fields, and the assembly itself. Metadata is used by the CLR to facilitate behavior, such as:

  • Validating an assembly before it is executed, or performing garbage collection while managed code is being executed.
  • Visual Basic developers use metadata to instruct the CLR how to behave at runtime.
  • Components referenced within applications have accompanying type libraries that contain metadata about the components, their methods, and their properties. You can use the Object Browser to view this information. (The information contained within the type library is what is used to drive IntelliSense.)

Better Support for Metadata

.NET refines the use of metadata within applications in three significant ways:

1. .NET consolidates the metadata associated with a component.
2. Because a .NET component does not have to be registered, installing and upgrading the component is easier and less problematic.
3. .NET makes a clear distinction between attributes that should only be set at compile time and those that can be modified at runtime.

Note
All attributes associated with Visual Basic components are represented in a common format and consolidated within the files that make up the assembly.

Because all metadata associated with a .NET component must reside within the file that contains the component, no global registration of components is required. When a new component is copied into an application's directory, it can be used immediately. Because the component and its associated metadata cannot become out of sync, upgrading the component is not an issue.

.NET makes a much better distinction between attributes that should be set at compile time and those that should be set at runtime. For example, whether a .NET component is serializable is determined at compile time. This setting cannot be overridden at runtime.

Attributes

Attributes are used to decorate entities such as assemblies, classes, methods, and properties with additional information. Attributes can be used for a variety of purposes. They can provide information, request a certain behavior at runtime, or even invoke a particular behavior from another application. An example of this can be demonstrated by using the Demo class defined in the following code block (code file: ProVB_AttributesModule1.vb):

Module Module1
  <Serializable()> 
  Public Class Demo
    <Obsolete("Use Method2 instead.")> 
    Public Sub Method1()
      ' Old implementation …
    End Sub
    Public Sub Method2()
      ' New implementation …
    End Sub
  End Class

  Public Sub Main()
    Dim d = New Demo()
    d.Method1()
  End Sub
End Module

Create a new console application for Visual Basic by selecting File ⇒ New Project and selecting Windows Console Application and then add a class into the file module1 by copying the previous code into Module1. A best practice is to place each class in its own source file, but in order to simplify this demonstration, the class Demo has been defined within the main module.

The first attribute on the Demo class marks the class with the Serializable attribute. The base class library will provide serialization support for instances of the Demo type. For example, the ResourceWriter type could be used to stream an instance of the Demo type to disk.

Method1 is prefaced by the Obsolete attribute. Method1 has been marked as obsolete, but it is still available. When a method is marked as obsolete, it is possible to instruct Visual Studio to prevent applications from compiling. However, a better strategy for large applications is to first mark a method or class as obsolete and then prevent its use in the next release. The preceding code causes Visual Studio to display a warning if Method1 is referenced within the application, as shown in Figure 2.1. Not only does the line with Method1 have a visual hint of the issue, but a warning message is visible in the Error List, with a meaningful message on how to correct the issue.

Figure 2.1 Visual Studio with IntelliSense warning visible

2.1

If the developer leaves this code unchanged and then compiles it, the application will compile correctly.

Sometimes you might need to associate multiple attributes with an entity. The following code shows an example of using both of the attributes from the previous example at the class level:

<Serializable(), Obsolete("No longer used.", True)> 
Public Class Demo
  ' Implementation …
End Class

Note in this case the Obsolete attribute has been modified to cause a compilation error by setting its second parameter to True. As shown in Figure 2.2, the compilation fails.

Figure 2.2 Visual Studio illustrating an obsolete class declaration error

2.2

Attributes play an important role in the development of .NET applications, particularly XML Web Services. As you'll see in Chapter 11, the declaration of a class as a Web service and of particular methods as Web methods are all handled through the use of attributes.

The Reflection API

Leveraging the presence of metadata throughout assemblies and classes, the .NET Framework provides the Reflection API. You can use the Reflection API to examine the metadata associated with an assembly and its types, even to examine the currently executing assembly or an assembly you would like to start while your application is running.

The Assembly class in the System.Reflection namespace can be used to access the metadata in an assembly. The LoadFrom method can be used to load an assembly, and the GetExecutingAssembly method can be used to access the currently executing assembly. The GetTypes method can then be used to obtain the collection of types defined in the assembly.

It's also possible to access the metadata of a type directly from an instance of that type. Because every object derives from System.Object, every object supports the GetType method, which returns a Type object that can be used to access the metadata associated with the type.

The Type object exposes many methods and properties for obtaining the metadata associated with a type. For example, you can obtain a collection of properties, methods, fields, and events exposed by the type by calling the GetMembers method. The Type object for the object's base type can also be obtained by calling the DeclaringType property. Reflection is covered in more detail in Chapter 17.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset