
Pro CSharp 2008 And The .NET 3.5 Platform [eng]
.pdf
522CHAPTER 15 ■ INTRODUCING .NET ASSEMBLIES
the *.config file, you can do so using the System.Configuration.Configuration class type. Consult the .NET Framework 3.5 SDK documentation for complete details.
■Source Code The AppConfigReaderApp application can be found under the Chapter 15 subdirectory.
The Machine Configuration File
The configuration files you’ve examined in this chapter have a common theme: they apply only to a specific application (that is why they have the same name as the launching application). In addition, each .NET-aware machine has a file named machine.config that contains a vast number of configuration details (many of which have nothing to do with resolving external assemblies) that control how a specific version of the .NET platform operates.
The .NET platform maintains a separate *.config file for each version of the framework installed on the local machine. For example, the machine.config file for .NET 2.0 can be found under the C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG directory (your version may differ). If you were to open this file, you would find numerous XML elements that control ASP.NET settings, various security details, debugging support, and so forth. However, if you wish to update the machine.config file with machinewide application settings (via an <appSettings> element), you are free to do so.
Although this file can be directly edited using Notepad, be warned that if you alter this file incorrectly, you may cripple the ability of a specific version of the runtime to function correctly. This scenario can be far more painful than a malformed application *.config file, given that XML errors in an application configuration file affect only a single application, but erroneous XML in the machine.config file can break a specific version of the .NET platform.
Summary
This chapter drilled down into the details of how the CLR resolves the location of externally referenced assemblies. You began by examining the content within an assembly: headers, metadata, manifests, and CIL. Then you constructed single-file and multifile assemblies and a handful of client applications (written in a language-agnostic manner).
As you have seen, assemblies may be private or shared. Private assemblies are copied to the client’s subdirectory, whereas shared assemblies are deployed to the GAC, provided they have been assigned a strong name. Finally, as you have seen, private and shared assemblies can be configured using a client-side XML configuration file or, alternatively, via a publisher policy assembly.


524 CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING
Figure 16-1. Viewing an assembly’s metadata
As you can see, ildasm.exe’s display of .NET type metadata is very verbose (the actual binary format is much more compact). In fact, if I were to list the entire metadata description representing the CarLibrary.dll assembly, it would span several pages. Given that this act would be a woeful waste of paper, let’s just glimpse into some key metadata descriptions of the CarLibrary.dll assembly.
Viewing (Partial) Metadata for the EngineState Enumeration
Each type defined within the current assembly is documented using a TypeDef #n token (where TypeDef is short for type definition). If the type being described uses a type defined within a separate
.NET assembly, the referenced type is documented using a TypeRef #n token (where TypeRef is short for type reference). A TypeRef token is a pointer (if you will) to the referenced type’s full metadata definition in an external library. In a nutshell, .NET metadata is a set of tables that clearly mark all type definitions (TypeDefs) and referenced entities (TypeRefs), all of which can be viewed using ildasm.exe’s metadata window.
As far as CarLibrary.dll goes, one TypeDef we encounter is the metadata description of the CarLibrary.EngineState enumeration (your number may differ; TypeDef numbering is based on the order in which the C# compiler processes the file):
TypeDef |
#1 |
|
|
------------------------------------------------------- |
|||
TypDefName: CarLibrary.EngineState (02000002) |
|||
Flags |
|
: [Public] [AutoLayout] [Class] [Sealed] [AnsiClass] (00000101) |
|
Extends |
: |
01000001 [TypeRef] System.Enum |
|
... |
|
|
|
Field |
#2 |
|
|
------------------------------------------------------- |
|||
Field |
Name: engineAlive (04000002) |
||
Flags |
|
: [Public] [Static] [Literal] [HasDefault] (00008056) |
|
DefltValue: (I4) 0 |
|||
CallCnvntn: [FIELD] |
|||
Field |
type: |
ValueClass CarLibrary.EngineState |
|
... |
|
|
|

CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING |
525 |
Here, the TypDefName token is used to establish the name of the given type. The Extends metadata token is used to document the base class of a given .NET type (in this case, the referenced type, System.Enum). Each field of an enumeration is marked using the Field #n token. For brevity, I have simply listed the metadata for EngineState.engineAlive.
Viewing (Partial) Metadata for the Car Type
Here is a partial dump of the Car type that illustrates the following:
•How fields are defined in terms of .NET metadata
•How methods are documented via .NET metadata
•How a single type property is mapped to two discrete member functions
TypeDef #3
-------------------------------------------------------
TypDefName: CarLibrary.Car (02000004)
Flags |
: [Public] [AutoLayout] [Class] [Abstract] [AnsiClass] (00100081) |
Extends |
: 01000002 [TypeRef] System.Object |
Field #1 |
|
------------------------------------------------------- |
|
Field |
Name: petName (04000008) |
Flags |
: [Family] (00000004) |
CallCnvntn: [FIELD] |
|
Field |
type: String |
... |
|
Method #1
-------------------------------------------------------
MethodName: .ctor (06000001)
Flags |
: [Public] [HideBySig] [ReuseSlot] [SpecialName] |
[RTSpecialName] [.ctor] (00001886) |
|
RVA |
: 0x00002050 |
ImplFlags : [IL] [Managed] (00000000) |
|
CallCnvntn: [DEFAULT] |
|
hasThis |
|
ReturnType: Void |
|
No arguments. |
|
... |
|
Property #1
-------------------------------------------------------
Prop.Name |
: PetName (17000001) |
Flags |
: [none] (00000000) |
CallCnvntn: [PROPERTY] |
|
hasThis |
|
ReturnType: String |
No arguments. |
|
|
DefltValue: |
|
|
Setter |
: (06000004) |
set_PetName |
Getter |
: (06000003) |
get_PetName |
0 Others |
|
|
...
First, note that the Car class metadata marks the type’s base class and includes various flags that describe how this type was constructed (e.g., [public], [abstract], and whatnot). Methods (such as our Car’s constructor) are described in regard to their parameters, return value, and name.

526 CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING
Finally, note how properties are mapped to their internal get/set methods using the .NET metadata Setter/Getter tokens. As you would expect, the derived Car types (SportsCar and MiniVan) are described in a similar manner.
Examining a TypeRef
Recall that an assembly’s metadata will describe not only the set of internal types (Car, EngineState, etc.), but also any external types the internal types reference. For example, given that CarLibrary. dll has defined two enumerations, you find a TypeRef block for the System.Enum type:
TypeRef #1 (01000001)
-------------------------------------------------------
Token: 0x01000001
ResolutionScope: 0x23000001
TypeRefName: System.Enum MemberRef #1
-------------------------------------------------------
Member: (0a00000f) ToString: CallCnvntn: [DEFAULT] hasThis
ReturnType: String No arguments.
Documenting the Defining Assembly
The ildasm.exe metadata window also allows you to view the .NET metadata that describes the assembly itself using the Assembly token. As you can see from the following (partial) listing, information documented within the Assembly table is (surprise, surprise!) the same information that can be viewable via the MANIFEST icon. Here is a partial dump of the manifest of CarLibrary.dll (version 2.0.0.0):
Assembly
-------------------------------------------------------
Token: 0x20000001
Name : CarLibrary
Public Key : 00 24 00 00 04 80 00 00 // Etc...
Hash Algorithm : 0x00008004
Major Version: 0x00000002
Minor Version: 0x00000000
Build Number: 0x00000000
Revision Number: 0x00000000
Locale: <null>
Flags : [SideBySideCompatible] (00000000)
Documenting Referenced Assemblies
In addition to the Assembly token and the set of TypeDef and TypeRef blocks, .NET metadata also makes use of AssemblyRef #n tokens to document each external assembly. Given that the
CarLibrary.dll makes use of the MessageBox type, you find an AssemblyRef for System.Windows. Forms, for example:

|
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING |
527 |
|
AssemblyRef #2 |
|
|
|
------------------------------------------------------- |
|
||
Token: |
0x23000002 |
|
|
Public |
Key or |
Token: b7 7a 5c 56 19 34 e0 89 |
|
Name: System.Windows.Forms |
|
||
Version: 2.0.3600.0 |
|
||
Major Version: 0x00000002 |
|
||
Minor Version: 0x00000000 |
|
||
Build Number: 0x00000e10 |
|
||
Revision Number: 0x00000000 |
|
||
Locale: <null> |
|
|
|
HashValue Blob: |
|
||
Flags: [none] |
(00000000) |
|
|
Documenting String Literals |
|
||
The final point of interest regarding .NET metadata is the fact that each and every string literal in |
|
||
your code base is documented under the User Strings token, for example: |
|
||
User Strings |
|
|
|
------------------------------------------------------- |
|
||
70000001 |
: (11) |
L"Jamming {0}" |
|
70000019 |
: (13) |
L"Quiet time..." |
|
70000035 |
: (23) |
L"CarLibrary Version 2.0!" |
|
70000065 |
: (14) |
L"Ramming speed!" |
|
70000083 |
: (19) |
L"Faster is better..." |
|
700000ab |
: (16) |
L"Time to call AAA" |
|
700000cd |
: (16) |
L"Your car is dead" |
|
Now, don’t be too concerned with the exact syntax of each and every piece of .NET metadata. The bigger point to absorb is that .NET metadata is very descriptive and lists each internally defined (and externally referenced) type found within a given code base.
The next question on your mind may be (in the best-case scenario) “How can I leverage this information in my applications?” or (in the worst-case scenario) “Why should I care about metadata?” To address both points of view, allow me to introduce .NET reflection services. Be aware that the usefulness of the topics presented over the pages that follow may be a bit of a head-scratcher until this chapter’s endgame. So hang tight.
■Note You will also find a number of CustomAttribute tokens displayed by the MetaInfo window, which documents the attributes applied within the code base. You’ll learn about the role of .NET attributes later in this chapter.
Understanding Reflection
In the .NET universe, reflection is the process of runtime type discovery. Using reflection services, you are able to programmatically obtain the same metadata information displayed by ildasm.exe using a friendly object model. For example, through reflection, you can obtain a list of all types contained within a given assembly (or *.netmodule), including the methods, fields, properties, and events defined by a given type. You can also dynamically discover the set of interfaces supported by a given type, the parameters of a method, and other related details (base classes, namespace information, manifest data, and so forth).

528 CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING
Like any namespace, System.Reflection contains a number of related types. Table 16-1 lists some of the core items you should be familiar with.
Table 16-1. A Sampling of Members of the System.Reflection Namespace
Type |
Meaning in Life |
Assembly |
This class contains a number of methods that allow you to load, investigate, |
|
and manipulate an assembly. |
AssemblyName |
This class allows you to discover numerous details behind an assembly’s |
|
identity (version information, culture information, and so forth). |
EventInfo |
This class holds information for a given event. |
FieldInfo |
This class holds information for a given field. |
MemberInfo |
This is the abstract base class that defines common behaviors for the |
|
EventInfo, FieldInfo, MethodInfo, and PropertyInfo types. |
MethodInfo |
This class contains information for a given method. |
Module |
This class allows you to access a given module within a multifile assembly. |
ParameterInfo |
This class holds information for a given parameter. |
PropertyInfo |
This class holds information for a given property. |
|
|
To understand how to leverage the System.Reflection namespace to programmatically read
.NET metadata, you need to first come to terms with the System.Type class.
The System.Type Class
The System.Type class defines a number of members that can be used to examine a type’s metadata, a great number of which return types from the System.Reflection namespace. For example, Type. GetMethods() returns an array of MethodInfo types, Type.GetFields() returns an array of FieldInfo types, and so on. The complete set of members exposed by System.Type is quite expansive; however, Table 16-2 offers a partial snapshot of the members supported by System.Type (see the .NET Framework 3.5 SDK documentation for full details).
Table 16-2. Select Members of System.Type
Type |
Meaning in Life |
IsAbstract |
These properties (among others) allow you to discover a number of |
IsArray |
basic traits about the Type you are referring to (e.g., if it is an |
IsClass |
abstract method, an array, a nested class, and so forth). |
IsCOMObject |
|
IsEnum |
|
IsGenericTypeDefinition |
|
IsGenericParameter |
|
IsInterface |
|
IsPrimitive |
|
IsNestedPrivate |
|
IsNestedPublic |
|
IsSealedIsValueType |
|

CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING |
529 |
||
|
|
|
|
Type |
Meaning in Life |
|
|
GetConstructors() |
These methods (among others) allow you to obtain an array |
|
|
GetEvents() |
representing the items (interface, method, property, etc.) you are |
|
|
GetFields() |
interested in. Each method returns a related array (e.g., GetFields() |
|
|
GetInterfaces() |
returns a FieldInfo array, GetMethods() returns a MethodInfo array, |
|
|
GetMembers() |
etc.). Be aware that each of these methods has a singular form (e.g., |
|
|
GetMethods() |
GetMethod(), GetProperty(), etc.) that allows you to retrieve a |
|
|
GetNestedTypes() |
specific item by name, rather than an array of all related items. |
|
|
GetProperties() |
|
|
|
FindMembers() |
This method returns an array of MemberInfo types based on search |
|
|
|
criteria. |
|
|
GetType() |
This static method returns a Type instance given a string name. |
|
|
InvokeMember() |
This method allows late binding to a given item. |
|
|
|
|
|
|
Obtaining a Type Reference Using System.Object.GetType()
You can obtain an instance of the Type class in a variety of ways. However, the one thing you cannot do is directly create a Type object using the new keyword, as Type is an abstract class. Regarding your first choice, recall that System.Object defines a method named GetType(), which returns an instance of the Type class that represents the metadata for the current object:
// Obtain type information using a SportsCar instance.
SportsCar sc = new SportsCar(); Type t = sc.GetType();
Obviously, this approach will only work if you have compile-time knowledge of the type (SportsCar in this case) and currently have an instance of the type in memory. Given this restriction, it should make sense that tools such as ildasm.exe do not obtain type information by directly calling System.Object.GetType() for each type, given the ildasm.exe was not compiled against your custom assemblies!
Obtaining a Type Reference Using System.Type.GetType()
To obtain type information in a more flexible manner, you may call the static GetType() member of the System.Type class and specify the fully qualified string name of the type you are interested in examining. Using this approach, you do not need to have compile-time knowledge of the type you are extracting metadata from, given that Type.GetType() takes an instance of the omnipresent
System.String.
■Note When I say you do not need compile-time knowledge when calling Type.GetType(), I am referring to the fact that this method can take any string value whatsoever (rather than a strongly typed variable). Of course, you would still need to know the name of the type in a stringified format!
The Type.GetType() method has been overloaded to allow you to specify two Boolean parameters, one of which controls whether an exception should be thrown if the type cannot be found, and the other of which establishes the case sensitivity of the string. To illustrate, ponder the following:
//Obtain type information using the static Type.GetType() method
//(don't throw an exception if SportsCar cannot be found and ignore case).
Type t = Type.GetType("CarLibrary.SportsCar", false, true);

530 CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING
In the previous example, notice that the string you are passing into GetType() makes no mention of the assembly containing the type. In this case, the assumption is that the type is defined within the currently executing assembly. However, when you wish to obtain metadata for a type within an external private assembly, the string parameter is formatted using the type’s fully qualified name, followed by the friendly name of the assembly containing the type (each of which is separated by a comma):
// Obtain type information for a type within an external assembly.
Type t = Type.GetType("CarLibrary.SportsCar, CarLibrary");
As well, do know that the string passed into Type.GetType() may specify a plus token (+) to denote a nested type. Assume you wish to obtain type information for an enumeration (SpyOptions) nested within a class named JamesBondCar. To do so, you would write the following:
//Obtain type information for a nested enumeration
//within the current assembly.
Type t = Type.GetType("CarLibrary.JamesBondCar+SpyOptions");
Obtaining a Type Reference Using typeof()
The final way to obtain type information is using the C# typeof operator:
// Get the Type using typeof.
Type t = typeof(SportsCar);
Like Type.GetType(), the typeof operator is helpful in that you do not need to first create an object instance to extract type information. However, your code base must still have compile-time knowledge of the type you are interested in examining, as typeof expects the strongly typed name of the type, rather than a textual representation of the type.
Building a Custom Metadata Viewer
To illustrate the basic process of reflection (and the usefulness of System.Type), let’s create a Console Application named MyTypeViewer. This program will display details of the methods, properties, fields, and supported interfaces (in addition to some other points of interest) for any type within mscorlib.dll (recall all .NET applications have automatic access to this core framework class library) or a type within MyTypeViewer itself. Once the application has been created, be sure to import the System.Reflection namespace.
using System.Reflection;
Reflecting on Methods
The Program class will be updated to define a number of static methods, each of which takes a single System.Type parameter and returns void. First you have ListMethods(), which (as you might guess) prints the name of each method defined by the incoming type. Notice how Type.GetMethods() returns an array of System.Reflection.MethodInfo types:
// Display method names of type. static void ListMethods(Type t)
{
Console.WriteLine("***** Methods *****");
MethodInfo[] mi = t.GetMethods(); foreach(MethodInfo m in mi)

CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING |
531 |
Console.WriteLine("->{0}", m.Name); Console.WriteLine();
}
Here, you are simply printing the name of the method using the MethodInfo.Name property. As you might guess, MethodInfo has many additional members that allow you to determine whether the method is static, virtual, generic, or abstract. As well, the MethodInfo type allows you to obtain the method’s return value and parameter set. You’ll spruce up the implementation of ListMethods() in just a bit.
Reflecting on Fields and Properties
The implementation of ListFields() is similar. The only notable difference is the call to Type. GetFields() and the resulting FieldInfo array. Again, to keep things simple, you are printing out only the name of each field.
// Display field names of type. static void ListFields(Type t)
{
Console.WriteLine("***** Fields *****");
FieldInfo[] fi = t.GetFields(); foreach(FieldInfo field in fi)
Console.WriteLine("->{0}", field.Name); Console.WriteLine();
}
The logic to display a type’s properties is similar:
// Display property names of type. static void ListProps(Type t)
{
Console.WriteLine("***** Properties *****");
PropertyInfo[] pi = t.GetProperties(); foreach(PropertyInfo prop in pi)
Console.WriteLine("->{0}", prop.Name); Console.WriteLine();
}
Reflecting on Implemented Interfaces
Next, you will author a method named ListInterfaces() that will print out the names of any interfaces supported on the incoming type. The only point of interest here is that the call to GetInterfaces() returns an array of System.Types! This should make sense given that interfaces are, indeed, types:
// Display implemented interfaces. static void ListInterfaces(Type t)
{
Console.WriteLine("***** Interfaces *****");
Type[] ifaces = t.GetInterfaces(); foreach(Type i in ifaces)
Console.WriteLine("->{0}", i.Name);
}