Tải bản đầy đủ (.pdf) (140 trang)

Pro C# 2008 and the .NET 3.5 Platform, Fourth Edition phần 5 pptx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (4.26 MB, 140 trang )

Displaying Various Odds and Ends
Last but not least, you have one final helper method that will simply display various statistics (indi-
cating whether the type is generic, what the base class is, whether the type is sealed, and so forth)
regarding the incoming type:
// Just for good measure.
static void ListVariousStats(Type t)
{
Console.WriteLine("***** Various Statistics *****");
Console.WriteLine("Base class is: {0}", t.
BaseType);
Console.WriteLine("Is type abstract? {0}", t.
IsAbstract);
Console.WriteLine("Is type sealed? {0}", t.
IsSealed);
Console.WriteLine("Is type generic? {0}", t.
IsGenericTypeDefinition);
Console.WriteLine("Is type a class type? {0}", t.
IsClass);
Console.WriteLine();
}
Implementing Main()
The Main() method of the Program class prompts the user for the fully qualified name of a type.
Once you obtain this string data, you pass it into the
Type.GetType() method and send the extracted
System.Type into each of your helper methods. This process repeats until the user enters Q to termi-
nate the application:
static void Main(string[] args)
{
Console.WriteLine("***** Welcome to MyTypeViewer *****");
string typeName = "";
bool userIsDone = false;


do
{
Console.WriteLine("\nEnter a type name to evaluate");
Console.Write("or enter Q to quit: ");
// Get name of type.
typeName = Console.ReadLine();
// Does user want to quit?
if (typeName.ToUpper() == "Q")
{
userIsDone = true;
break;
}
// Try to display type.
try
{
Type t = Type.GetType(typeName);
Console.WriteLine("");
ListVariousStats(t);
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING532
8849CH16.qxd 10/9/07 4:27 PM Page 532
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
ListFields(t);
ListProps(t);
ListMethods(t);
ListInterfaces(t);
}
catch
{
Console.WriteLine("Sorry, can't find type");

}
} while (!userIsDone);
}
At this point, MyTypeViewer.exe is ready to take out for a test drive. For example, run your appli-
cation and enter the following fully qualified names (be aware that the manner in which you invoked
Type.GetType() requires case-sensitive string names):

System.Int32
• System.Collections.ArrayList
• System.Threading.Thread
• System.Void
• System.IO.BinaryWriter
• System.Math
• System.Console
• MyTypeViewer.Program
Figure 16-2 shows the partial output when specifying System.Math.
Figure 16-2. Reflecting on System.Math
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 533
8849CH16.qxd 10/9/07 4:27 PM Page 533
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
Reflecting on Generic Types
When you call Type.GetType() in order to obtain metadata descriptions of generic types, you must
make use of a special syntax involving a “back tick” character (
`) followed by a numerical value
that represents the number of type parameters the type supports. For example, if you wish to print
out the metadata description of
List<T>, you would need to pass the following string into your
application:
System.Collections.Generic.List`1

Here, we are using the numerical value of 1, given that List<T> has only one type parameter.
However, if you wish to reflect over
Dictionary<TKey, TValue>, you would supply the value 2:
System.Collections.Generic.Dictionary`2
Reflecting on Method Parameters and Return Values
So far, so good! Let’s make one minor enhancement to the current application. Specifically, you will
update the
ListMethods() helper function to list not only the name of a given method, but also the
return value and incoming parameters. The
MethodInfo type provides the ReturnType property and
GetParameters() method for these very tasks. In the following code, notice that you are building a
string type that contains the type and name of each parameter using a nested
foreach loop:
static void ListMethods(Type t)
{
Console.WriteLine("***** Methods *****");
MethodInfo[] mi = t.GetMethods();
foreach (MethodInfo m in mi)
{
// Get return value.
string retVal = m.ReturnType.FullName;
string paramInfo = "(";
// Get params.
foreach (ParameterInfo pi in m.GetParameters())
{
paramInfo += string.Format("{0} {1} ", pi.ParameterType, pi.Name);
}
paramInfo += ")";
// Now display the basic method sig.
Console.WriteLine("->{0} {1} {2}", retVal, m.Name, paramInfo);

}
Console.WriteLine();
}
If you now run this updated application, you will find that the methods of a given type are
much more detailed. Figure 16-3 shows the method metadata of the
System.Globalization.
GregorianCalendar
type.
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING534
8849CH16.qxd 10/9/07 4:27 PM Page 534
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
Figure 16-3. Method details of System.Globalization.GregorianCalendar
The current implementation of ListMethods() is helpful, in that you can directly investigate
each parameter and method return value using the
System.Reflection object model. As an extreme
shortcut, be aware that each of the
XXXInfo types (MethodInfo, PropertyInfo, EventInfo, etc.) have
overridden
ToString() to display the signature of the item requested. Thus, we could also imple-
ment
ListMethods() as follows:
public static void ListMethods(Type t)
{
Console.WriteLine("***** Methods *****");
MethodInfo[] mi = t.GetMethods();
foreach (MethodInfo m in mi)
{
// Could also simply say "Console.WriteLine(m)" as well,
// as ToString() is called automatically by WriteLine().

Console.WriteLine(m.ToString());
}
Console.WriteLine();
}
Interesting stuff, huh? Clearly the System.Reflection namespace and System.Type class allow
you to reflect over many other aspects of a type beyond what
MyTypeViewer is currently displaying.
As you would hope, you can obtain a type’s events, get the list of any generic parameters for a given
member, and glean dozens of other details.
Nevertheless, at this point you have created a (somewhat capable) object browser. The major
limitation, of course, is that you have no way to reflect beyond the current assembly (
MyTypeViewer)
or the always accessible
mscorlib.dll. This begs the question, “How can I build applications that
can load (and reflect over) assemblies not referenced at compile time?”
■Source Code The MyTypeViewer project can be found under the Chapter 16 subdirectory.
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 535
8849CH16.qxd 10/9/07 4:27 PM Page 535
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
Dynamically Loading Assemblies
In the previous chapter, you learned all about how the CLR consults the assembly manifest when
probing for an externally referenced assembly. However, there will be many times when you need
to load assemblies on the fly programmatically, even if there is no record of said assembly in the
manifest. Formally speaking, the act of loading external assemblies on demand is known as a
dynamic load.
System.Reflection defines a class named Assembly. Using this type, you are able to dynamically
load an assembly as well as discover properties about the assembly itself. Using the
Assembly type,
you are able to dynamically load private or shared assemblies, as well as load an assembly located

at an arbitrary location. In essence, the
Assembly class provides methods (Load() and LoadFrom() in
particular) that allow you to programmatically supply the same sort of information found in a
client-side
*.config file.
To illustrate dynamic loading, create a brand-new Console Application named External
AssemblyReflector. Your task is to construct a
Main() method that prompts for the friendly name of
an assembly to load dynamically. You will pass the
Assembly reference into a helper method named
DisplayTypes(), which will simply print the names of each class, interface, structure, enumeration,
and delegate it contains. The code is refreshingly simple:
using System;
using System.Reflection;
using System.IO;
// For FileNotFoundException definition.
namespace ExternalAssemblyReflector
{
class Program
{
static void DisplayTypesInAsm(Assembly asm)
{
Console.WriteLine("\n***** Types in Assembly *****");
Console.WriteLine("->{0}", asm.FullName);
Type[] types = asm.GetTypes();
foreach (Type t in types)
Console.WriteLine("Type: {0}", t);
Console.WriteLine("");
}
static void Main(string[] args)

{
Console.WriteLine("***** External Assembly Viewer *****");
string asmName = "";
bool userIsDone = false;
Assembly asm = null;
do
{
Console.WriteLine("\nEnter an assembly to evaluate");
Console.Write("or enter Q to quit: ");
// Get name of assembly.
asmName = Console.ReadLine();
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING536
8849CH16.qxd 10/9/07 4:27 PM Page 536
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
// Does user want to quit?
if (asmName.ToUpper() == "Q")
{
userIsDone = true;
break;
}
// Try to load assembly.
try
{
asm = Assembly.Load(asmName);
DisplayTypesInAsm(asm);
}
catch
{
Console.WriteLine("Sorry, can't find assembly.");

}
} while (!userIsDone);
}
}
}
Notice that the static Assembly.Load() method has been passed only the friendly name of the
assembly you are interested in loading into memory. Thus, if you wish to reflect over
CarLibrary.
dll
, you will need to copy the CarLibrary.dll binary to the \bin\Debug directory of the External
AssemblyReflector application to run this program. Once you do, you will find output similar to
Figure 16-4.
Figure 16-4. Reflecting on the external CarLibrary assembly
If you wish to make ExternalAssemblyReflector more flexible, you can update your code to load
the external assembly using
Assembly.LoadFrom() rather than Assembly.Load(). By doing so, you can
enter an absolute path to the assembly you wish to view (e.g., C:\MyApp\MyAsm.dll). Essentially,
Assembly.LoadFrom() allows you to programmatically supply a <codeBase> value.
■Source Code The ExternalAssemblyReflector project is included in the Chapter 16 subdirectory.
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 537
8849CH16.qxd 10/9/07 4:27 PM Page 537
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
Reflecting on Shared Assemblies
The Assembly.Load() method has been overloaded a number of times. One variation allows you to
specify a culture value (for localized assemblies) as well as a version number and public key token
value (for shared assemblies). Collectively speaking, the set of items identifying an assembly is
termed the
display name. The format of a display name is a comma-delimited string of name/value
pairs that begins with the friendly name of the assembly, followed by optional qualifiers (that may

appear in any order). Here is the template to follow (optional items appear in parentheses):
Name (,Version = major.minor.build.revision) (,Culture = culture token)
(,
PublicKeyToken= public key token)
When you’re crafting a display name, the convention PublicKeyToken=null indicates that bind-
ing and matching against a non–strongly named assembly is required. Additionally,
Culture=""
indicates matching against the default culture of the target machine, for example:
// Load version 1.0.982.23972 of CarLibrary using the default culture.
Assembly a = Assembly.Load(
@"CarLibrary, Version=1.0.982.23972, PublicKeyToken=null, Culture=""");
Also be aware that the System.Reflection namespace supplies the AssemblyName type, which
allows you to represent the preceding string information in a handy object variable. Typically, this
class is used in conjunction with
System.Version, which is an OO wrapper around an assembly’s
version number. Once you have established the display name, it can then be passed into the over-
loaded
Assembly.Load() method:
// Make use of AssemblyName to define the display name.
AssemblyName asmName;
asmName = new AssemblyName();
asmName.Name = "CarLibrary";
Version v = new Version("1.0.982.23972");
asmName.Version = v;
Assembly a = Assembly.Load(asmName);
To load a shared assembly from the GAC, the Assembly.Load() parameter must specify a
PublicKeyToken value. For example, assume you wish to load version 2.0.0.0 of the System.Windows.
Forms.dll
assembly provided by the .NET base class libraries. Given that the number of types in this
assembly is quite large, the following application only prints out the names of public enums, using

a simple LINQ query:
using System;
using System.Reflection;
using System.IO;
using System.Linq;
namespace SharedAsmReflector
{
public class SharedAsmReflector
{
private static void DisplayInfo(Assembly a)
{
Console.WriteLine("***** Info about Assembly *****");
Console.WriteLine("Loaded from GAC? {0}", a.GlobalAssemblyCache);
Console.WriteLine("Asm Name: {0}", a.GetName().Name);
Console.WriteLine("Asm Version: {0}", a.GetName().Version);
Console.WriteLine("Asm Culture: {0}",
a.GetName().CultureInfo.DisplayName);
Console.WriteLine("\nHere are the public enums:");
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING538
8849CH16.qxd 10/9/07 4:27 PM Page 538
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
// Use a LINQ query to find the public enums.
Type[] types = a.GetTypes();
var publicEnums = from pe in types where pe.IsEnum &&
pe.IsPublic select pe;
foreach (var pe in publicEnums)
{
Console.WriteLine(pe);
}

}
static void Main(string[] args)
{
Console.WriteLine("***** The Shared Asm Reflector App *****\n");
// Load System.Windows.Forms.dll from GAC.
string displayName = null;
displayName = "System.Windows.Forms," +
"Version=2.0.0.0," +
"PublicKeyToken=b77a5c561934e089," +
@"Culture=""";
Assembly asm = Assembly.Load(displayName);
DisplayInfo(asm);
Console.WriteLine("Done!");
Console.ReadLine();
}
}
}
■Source Code The SharedAsmReflector project is included in the Chapter 16 subdirectory.
At this point you should understand how to use some of the core types defined within the
System.Reflection namespace to discover metadata at runtime. Of course, I realize despite the
“cool factor,” you likely will not need to build custom object browsers at your place of employment.
Do recall, however, that reflection services are the foundation for a number of very common pro-
gramming activities, including
late binding.
Understanding Late Binding
Simply put, late binding is a technique in which you are able to create an instance of a given type
and invoke its members at runtime without having hard-coded compile-time knowledge of its exis-
tence. When you are building an application that binds late to a type in an external assembly, you
have no reason to set a reference to the assembly; therefore, the caller’s manifest has no direct list-
ing of the assembly.

At first glance, it is not easy to see the value of late binding. It is true that if you can “bind early”
to a type (e.g., set an assembly reference and allocate the type using the C#
new keyword), you should
opt to do so. For one reason, early binding allows you to determine errors at compile time, rather
than at runtime. Nevertheless, late binding does have a critical role in any extendable application
you may be building. You will have a chance to build such an “extendable” program at the end of
this chapter in the section “Building an Extendable Application”; until then, we need to examine the
role of the
Activator type.
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 539
8849CH16.qxd 10/9/07 4:27 PM Page 539
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
The System.Activator Class
The System.Activator class is the key to the .NET late binding process. Beyond the methods inher-
ited from
System.Object, Activator defines only a small set of members, many of which have to do
with the .NET remoting API. For our current example, we are only interested in the
Activator.
CreateInstance()
method, which is used to create an instance of a type à la late binding.
This method has been overloaded numerous times to provide a good deal of flexibility. The
simplest variation of the
CreateInstance() member takes a valid Type object that describes the
entity you wish to allocate on the fly. Create a new Console Application named LateBindingApp,
and update the
Main() method as follows (be sure to place a copy of CarLibrary.dll in the project’s
\bin\Debug directory):
// Create a type dynamically.
public class Program

{
static void Main(string[] args)
{
Console.WriteLine("***** Fun with Late Binding *****");
// Try to load a local copy of CarLibrary.
Assembly a = null;
try
{
a = Assembly.Load("CarLibrary");
}
catch(FileNotFoundException e)
{
Console.WriteLine(e.Message);
return;
}
// Get metadata for the Minivan type.
Type miniVan = a.GetType("CarLibrary.MiniVan");
// Create the Minivan on the fly.
object obj = Activator.CreateInstance(miniVan);
Console.WriteLine("Created a {0} using late binding!", obj);
Console.ReadLine();
}
}
Notice that the Activator.CreateInstance() method returns a System.Object rather than a
strongly typed
MiniVan. Therefore, if you apply the dot operator on the obj variable, you will fail to
see any members of the
MiniVan type. At first glance, you may assume you can remedy this problem
with an explicit cast; however, this program has no clue what a
MiniVan is in the first place (and if

you did, why use late binding at all)!
Remember that the whole point of late binding is to create instances of objects for which there
is no compile-time knowledge. Given this, how can you invoke the underlying methods of the
MiniVan object stored in the System.Object variable? The answer, of course, is by using reflection.
Invoking Methods with No Parameters
Assume you wish to invoke the TurboBoost() method of the MiniVan. As you recall, this method will
set the state of the engine to “dead” and display an informational message box. The first step is to
obtain a
MethodInfo type for the TurboBoost() method using Type.GetMethod(). From the resulting
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING540
8849CH16.qxd 10/9/07 4:27 PM Page 540
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
MethodInfo, you are then able to call MiniVan.TurboBoost using Invoke(). MethodInfo.Invoke()
requires you to send in all parameters that are to be given to the method represented by MethodInfo.
These parameters are represented by an array of
System.Object types (as the parameters for a given
method could be any number of various entities).
Given that
TurboBoost() does not require any parameters, you can simply pass null (meaning
“this method has no parameters”). Update your
Main() method as follows:
static void Main(string[] args)
{

// Get metadata for the Minivan type.
Type miniVan = a.GetType("CarLibrary.MiniVan");
// Create the Minivan on the fly.
object obj = Activator.CreateInstance(miniVan);
Console.WriteLine("Created a {0} using late binding!", obj);

// Get info for TurboBoost.
MethodInfo mi = miniVan.GetMethod("TurboBoost");
// Invoke method ('null' for no parameters).
mi.Invoke(obj, null);
Console.ReadLine();
}
At this point you are happy to see the message box in Figure 16-5.
Figure 16-5. Late-bound method invocation
Invoking Methods with Parameters
To illustrate how to dynamically invoke a method that does take some number of parameters,
assume you have updated the
MiniVan type created in the previous chapter with a new method
named
TellChildToBeQuiet():
// Quiet down the troops
public void TellChildToBeQuiet(string kidName, int shameIntensity)
{
for(int i = 0 ; i < shameIntensity; i++)
MessageBox.Show(string.Format("Be quiet {0} !!", kidName));
}
TellChildToBeQuiet()
takes two parameters: a string representing the child’s name and an
integer representing your current level of frustration. When using late binding, parameters are
packaged as an array of
System.Objects. To invoke the new method, update the Main() method as
follows:
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 541
8849CH16.qxd 10/9/07 4:27 PM Page 541
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -

static void Main(string[] args)
{

// Get metadata for the Minivan type.
Type miniVan = a.GetType("CarLibrary.MiniVan");
// Create the Minivan on the fly.
object obj = Activator.CreateInstance(miniVan);
Console.WriteLine("Created a {0} using late binding!", obj);
// Bind late to a method taking params.
object[] paramArray = new object[2];
paramArray[0] = "Fred"; // Child name.
paramArray[1] = 4; // Shame Intensity.
mi = miniVan.GetMethod("TellChildToBeQuiet");
mi.Invoke(obj, paramArray);
Console.ReadLine();
}
If you run this program, you will see four message boxes pop up, shaming young Fred. Hope-
fully at this point you can see the relationships among reflection, dynamic loading, and late binding.
Again, you still may wonder exactly
when you might make use of these techniques in your own
applications. The conclusion of this chapter should shed light on this question; however, the next
topic under investigation is the role of .NET attributes.
■Source Code The LateBindingApp project is included in the Chapter 16 subdirectory.
Understanding Attributed Programming
As illustrated at beginning of this chapter, one role of a .NET compiler is to generate metadata
descriptions for all defined and referenced types. In addition to this standard metadata contained
within any assembly, the .NET platform provides a way for programmers to embed additional
metadata into an assembly using
attributes. In a nutshell, attributes are nothing more than code
annotations that can be applied to a given type (class, interface, structure, etc.), member (property,

method, etc.), assembly, or module.
The idea of annotating code using attributes is not new. COM IDL provided numerous prede-
fined attributes that allowed developers to describe the types contained within a given COM server.
However, COM attributes were little more than a set of keywords. If a COM developer needed to
create a custom attribute, he or she could do so, but it was referenced in code by a 128-bit number
(GUID), which was cumbersome at best.
Unlike COM IDL attributes (which again were simply keywords), .NET attributes are class types
that extend the abstract
System.Attribute base class. As you explore the .NET namespaces, you will
find many predefined attributes that you are able to make use of in your applications. Furthermore,
you are free to build custom attributes to further qualify the behavior of your types by creating a
new type deriving from
Attribute.
Understand that when you apply attributes in your code, the embedded metadata is essentially
useless until another piece of software explicitly reflects over the information. If this is not the case,
the blurb of metadata embedded within the assembly is ignored and completely harmless.
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING542
8849CH16.qxd 10/9/07 4:27 PM Page 542
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
Attribute Consumers
As you would guess, the .NET 3.5 Framework SDK ships with numerous utilities that are indeed on
the lookout for various attributes. The C# compiler (
csc.exe) itself has been preprogrammed to
discover the presence of various attributes during the compilation cycle. For example, if the C#
compiler encounters the
[CLSCompliant] attribute, it will automatically check the attributed item to
ensure it is exposing only CLS-compliant constructs. By way of another example, if the C# compiler
discovers an item attributed with the
[Obsolete] attribute, it will display a compiler warning in the

Visual Studio 2008 Error List window.
In addition to development tools, numerous methods in the .NET base class libraries are pre-
programmed to reflect over specific attributes. For example, if you wish to persist the state of an
object to file, all you are required to do is annotate your class with the
[Serializable] attribute. If
the
Serialize() method of the BinaryFormatter class encounters this attribute, the object is auto-
matically persisted to file in a compact binary format.
The .NET CLR is also on the prowl for the presence of certain attributes. Perhaps the most
famous .NET attribute is
[WebMethod]. If you wish to expose a method via HTTP requests and auto-
matically encode the method return value as XML, simply apply
[WebMethod] to the method and the
CLR handles the details. Beyond web service development, attributes are critical to the operation of
the .NET security system, Windows Communication Foundation, and COM/.NET interoperability
(and so on).
Finally, you are free to build applications that are programmed to reflect over your own custom
attributes as well as any attribute in the .NET base class libraries. By doing so, you are essentially
able to create a set of “keywords” that are understood by a specific set of assemblies.
Applying Attributes in C#
As previously mentioned, the .NET base class library provides a number of attributes in various
namespaces. Table 16-3 gives a snapshot of some—but by
absolutely no means all—predefined
attributes.
Table 16-3. A Tiny Sampling of Predefined Attributes
Attribute Meaning in Life
[CLSCompliant] Enforces the annotated item to conform to the rules of the Common
Language Specification (CLS). Recall that CLS-compliant types are
guaranteed to be used seamlessly across all .NET programming languages.
[DllImport] Allows .NET code to make calls to any unmanaged C- or C++-based code

library, including the API of the underlying operating system. Do note that
[DllImport] is not used when communicating with COM-based software.
[Obsolete] Marks a deprecated type or member. If other programmers attempt to use
such an item, they will receive a compiler warning describing the error of
their ways.
[Serializable] Marks a class or structure as being “serializable,” meaning it is able to persist
its current state into a stream.
[NonSerialized] Specifies that a given field in a class or structure should not be persisted
during the serialization process.
[WebMethod] Marks a method as being invokable via HTTP requests and instructs the CLR
to serialize the method return value as XML.
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 543
8849CH16.qxd 10/9/07 4:27 PM Page 543
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
To illustrate the process of applying attributes in C#, assume you wish to build a class named
Motorcycle that can be persisted in a binary format. To do so, simply apply the [Serializable]
attribute to the class definition. If you have a field that should not be persisted, you may apply the
[NonSerialized] attribute:
// This class can be saved to disk.
[Serializable]
public class Motorcycle
{
// However this field will not be persisted.
[NonSerialized]
float weightOfCurrentPassengers;
// These fields are still serializable.
bool hasRadioSystem;
bool hasHeadSet;
bool hasSissyBar;

}
■Note An attribute only applies to the “very next” item. For example, the only nonserialized field of the
Motorcycle class is weightOfCurrentPassengers. The remaining fields are serializable given that the
entire class has been annotated with [Serializable].
At this point, don’t concern yourself with the actual process of object serialization (Chapter 21
examines the details). Just notice that when you wish to apply an attribute, the name of the attrib-
ute is sandwiched between square brackets.
Once this class has been compiled, you can view the extra metadata using
ildasm.exe. Notice
that these attributes are recorded using the
serializable and notserialized tokens (see Figure 16-6).
Figure 16-6. Attributes shown in ildasm.exe
As you might guess, a single item can be attributed with multiple attributes. Assume you have a
legacy C# class type (
HorseAndBuggy) that was marked as serializable, but is now considered obsolete
for current development. Rather than deleting the class definition from your code base (and risk
breaking existing software), you can mark the class with the
[Obsolete] attribute. To apply multiple
attributes to a single item, simply use a comma-delimited list:
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING544
8849CH16.qxd 10/9/07 4:27 PM Page 544
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
[Serializable, Obsolete("Use another vehicle!")]
public class HorseAndBuggy
{
//
}
As an alternative, you can also apply multiple attributes on a single item by stacking each
attribute as follows (the end result is identical):

[Serializable]
[Obsolete("Use another vehicle!")]
public class HorseAndBuggy
{
//
}
Specifying Constructor Parameters for Attributes
Notice that the [Obsolete] attribute is able to accept what appears to be a constructor parameter.
If you view the formal definition of the
[Obsolete] attribute using the Code Definition window of
Visual Studio 2008, you will find that this class indeed provides a constructor receiving a
System.
String
:
public sealed class ObsoleteAttribute : System.Attribute
{
public bool IsError { get; }
public string Message { get; }
public ObsoleteAttribute(string message, bool error);
public ObsoleteAttribute(string message);
public ObsoleteAttribute();
}
Understand that when you supply constructor parameters to an attribute, the attribute is not
allocated into memory until the parameters are reflected upon by another type or an external tool.
The string data defined at the attribute level is simply stored within the assembly as a blurb of
metadata.
The Obsolete Attribute in Action
Now that HorseAndBuggy has been marked as obsolete, if you were to allocate an instance of this
type:
static void Main(string[] args)

{
HorseAndBuggy mule = new HorseAndBuggy();
}
you would find that the supplied string data is extracted and displayed within the Error List window
of Visual Studio 2008 (see Figure 16-7).
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 545
8849CH16.qxd 10/9/07 4:27 PM Page 545
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
Figure 16-7. Attributes in action
In this case, the “other piece of software” that is reflecting on the [Obsolete] attribute is the C#
compiler.
C# Attribute Shorthand Notation
If you were reading closely, you may have noticed that the actual class name of the [Obsolete]
attribute is ObsoleteAttribute, not Obsolete. As a naming convention, all .NET attributes (including
custom attributes you may create yourself) are suffixed with the
Attribute token. However, to
simplify the process of applying attributes, the C# language does not require you to type in the
Attribute suffix. Given this, the following iteration of the HorseAndBuggy type is identical to the
previous (it just involves a few more keystrokes):
[SerializableAttribute]
[ObsoleteAttribute("Use another vehicle!")]
public class HorseAndBuggy
{
//
}
Be aware that this is a courtesy provided by C#. Not all .NET-enabled languages support this
shorthand attribute syntax. In any case, at this point you should hopefully understand the following
key points regarding .NET attributes:
• Attributes are classes that derive from

System.Attribute.
• Attributes result in embedded metadata.
• Attributes are basically useless until another agent reflects upon them.
• Attributes are applied in C# using square brackets.
Next up, let’s examine how you can build your own custom attributes and a piece of custom
software that reflects over the embedded metadata.
Building Custom Attributes
The first step in building a custom attribute is to create a new class deriving from System.Attribute.
Keeping in step with the automobile theme used throughout this book, assume you have created a
brand new C# class library named
AttributedCarLibrary. This assembly will define a handful of
vehicles (some of which you have already seen in this text), each of which is described using a cus-
tom attribute named
VehicleDescriptionAttribute:
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING546
8849CH16.qxd 10/9/07 4:27 PM Page 546
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
// A custom attribute.
public sealed class VehicleDescriptionAttribute : System.Attribute
{
private string msgData;
public VehicleDescriptionAttribute(string description)
{ msgData = description;}
public VehicleDescriptionAttribute(){ }
public string Description
{
get { return msgData; }
set { msgData = value; }
}

}
As you can see, VehicleDescriptionAttribute maintains a private internal string (msgData)
that can be set using a custom constructor and manipulated using a type property (
Description).
Beyond the fact that this class derived from
System.Attribute, there is nothing unique to this class
definition.
■Note For security reasons, it is considered a .NET best practice to design all custom attributes as sealed. In
fact, Visual Studio 2008 provides a code snippet named
Attribute that will dump out a new System.
Attribute-derived class into your code window. See Chapter 2 for an explication of using code snippets.
Applying Custom Attributes
Given that VehicleDescriptionAttribute is derived from System.Attribute, you are now able to
annotate your vehicles as you see fit. For testing purposes, add the following class definitions to
your new class library:
// Assign description using a "named property."
[Serializable]
[
VehicleDescription(Description = "My rocking Harley")]
public class Motorcycle
{
}
[SerializableAttribute]
[ObsoleteAttribute("Use another vehicle!")]
[
VehicleDescription("The old gray mare, she ain't what she used to be ")]
public class HorseAndBuggy
{
}
[

VehicleDescription("A very long, slow, but feature-rich auto")]
public class Winnebago
{
}
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 547
8849CH16.qxd 10/9/07 4:27 PM Page 547
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
Named Property Syntax
Notice that the description of the Motorcycle is assigned a description using a new bit of attribute-
centric syntax termed a
named property. In the constructor of the first [VehicleDescription]
attribute, you set the underlying System.String using a name/value pair. If this attribute is reflected
upon by an external agent, the value is fed into the
Description property (named property syntax is
legal only if the attribute supplies a writable .NET property).
In contrast, the
HorseAndBuggy and Winnebago types are not making use of named property syn-
tax and are simply passing the string data via the custom constructor. In any case, once you compile
the
AttributedCarLibrary assembly, you can make use of ildasm.exe to view the injected metadata
descriptions for your type. For example, Figure 16-8 shows an embedded description of the
Winnebago type.
Figure 16-8. Embedded vehicle description data
Restricting Attribute Usage
By default, custom attributes can be applied to just about any aspect of your code (methods,
classes, properties, and so on). Thus, if it made sense to do so, you could use
VehicleDescription
to qualify methods, properties, or fields (among other things):
[VehicleDescription("A very long, slow, but feature-rich auto")]

public class Winnebago
{
[VehicleDescription("My rocking CD player")]
public void PlayMusic(bool On)
{

}
}
In some cases, this is exactly the behavior you require. Other times, however, you may want to
build a custom attribute that can be applied only to select code elements. If you wish to constrain
the scope of a custom attribute, you will need to apply the
[AttributeUsage] attribute on the
definition of your custom attribute. The
[AttributeUsage] attribute allows you to supply any
combination of values (via an OR operation) from the
AttributeTargets enumeration:
// This enumeration defines the possible targets of an attribute.
public enum AttributeTargets
{
All, Assembly, Class, Constructor,
Delegate, Enum, Event, Field,
Interface, Method, Module, Parameter,
Property, ReturnValue, Struct
}
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING548
8849CH16.qxd 10/9/07 4:27 PM Page 548
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
Furthermore, [AttributeUsage] also allows you to optionally set a named property
(

AllowMultiple) that specifies whether the attribute can be applied more than once on the same
item. As well,
[AttributeUsage] allows you to establish whether the attribute should be inherited
by derived classes using the
Inherited named property.
To establish that the
[VehicleDescription] attribute can be applied only once on a class or
structure (and the value is not inherited by derived types), you can update the
VehicleDescriptionAttribute definition as follows:
// This time, we are using the AttributeUsage attribute
// to annotate our custom attribute.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct,
AllowMultiple = false, Inherited = false)]
public sealed class VehicleDescriptionAttribute : System.Attribute
{

}
With this, if a developer attempted to apply the [VehicleDescription] attribute on anything
other than a class or structure, he or she is issued a compile-time error.
■Tip Always get in the habit of explicitly marking the usage flags for any custom attribute you may create, as not
all .NET programming languages honor the use of unqualified attributes!
Assembly-Level (and Module-Level) Attributes
It is also possible to apply attributes on all types within a given module (for a multifile assembly) or
all modules within a given assembly using the
[module:] and [assembly:] tags, respectively. For
example, assume you wish to ensure that every public type defined within your assembly is CLS
compliant. To do so, simply add the following line in any one of your C# source code files (do note
that assembly-level attributes must be outside the scope of a namespace definition):
// Enforce CLS compliance for all public types in this assembly.
[assembly:System.CLSCompliantAttribute(true)]

If you now add a bit of code that falls outside the CLS specification (such as an exposed point
of unsigned data):
// Ulong types don't jibe with the CLS.
public class Winnebago
{
public
ulong notCompliant;
}
you are issued a compiler warning.
The Visual Studio 2008 AssemblyInfo.cs File
By default, Visual Studio 2008 projects receive a file named AssemblyInfo.cs, which can be viewed
by expanding the Properties icon of the Solution Explorer (see Figure 16-9).
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 549
8849CH16.qxd 10/9/07 4:27 PM Page 549
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
Figure 16-9. The AssemblyInfo.cs file
This file is a handy place to put attributes that are to be applied at the assembly level. You may
recall from Chapter 15, during our examination of .NET assemblies, that the manifest contains
assembly-level metadata, much of which comes from the assembly-level attributes shown in
Table 16-4.
Table 16-4. Select Assembly-Level Attributes
Attribute Meaning in Life
AssemblyCompanyAttribute Holds basic company information
AssemblyCopyrightAttribute Holds any copyright information for the product or
assembly
AssemblyCultureAttribute Provides information on what cultures or languages the
assembly supports
AssemblyDescriptionAttribute Holds a friendly description of the product or modules
that make up the assembly

AssemblyKeyFileAttribute Specifies the name of the file containing the key pair used
to sign the assembly (i.e., establish a strong name)
AssemblyOperatingSystemAttribute Provides information on which operating system the
assembly was built to support
AssemblyProcessorAttribute Provides information on which processors the assembly
was built to support
AssemblyProductAttribute Provides product information
AssemblyTrademarkAttribute Provides trademark information
AssemblyVersionAttribute Specifies the assembly’s version information, in the
format <major.minor.build.revision>
■Source Code The AttributedCarLibrary project is included in the Chapter 16 subdirectory.
Reflecting on Attributes Using Early Binding
As mentioned in this chapter, an attribute is quite useless until some piece of software reflects
over its values. Once a given attribute has been discovered, that piece of software can take
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING550
8849CH16.qxd 10/9/07 4:27 PM Page 550
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
whatever course of action necessary. Now, like any application, this “other piece of software”
could discover the presence of a custom attribute using either early binding or late binding. If you
wish to make use of early binding, you’ll require the client application to have a compile-time
definition of the attribute in question (
VehicleDescriptionAttribute in this case). Given that the
AttributedCarLibrary assembly has defined this custom attribute as a public class, early binding is
the best option.
To illustrate the process of reflecting on custom attributes, create a new C# Console Applica-
tion named VehicleDescriptionAttributeReader. Next, set a reference to the
AttributedCarLibrary
assembly. Finally, update your initial *.cs file with the following code:
// Reflecting on custom attributes using early binding.

using System;
using AttributedCarLibrary;
public class Program
{
static void Main(string[] args)
{
// Get a Type representing the Winnebago.
Type t = typeof(Winnebago);
// Get all attributes on the Winnebago.
object[] customAtts = t.GetCustomAttributes(false);
// Print the description.
Console.WriteLine("***** Value of VehicleDescriptionAttribute *****\n");
foreach(VehicleDescriptionAttribute v in customAtts)
Console.WriteLine("-> {0}\n", v.Description);
Console.ReadLine();
}
}
As the name implies, Type.GetCustomAttributes() returns an object array that represents all
the attributes applied to the member represented by the
Type (the Boolean parameter controls
whether the search should extend up the inheritance chain). Once you have obtained the list of
attributes, iterate over each
VehicleDescriptionAttribute class and print out the value obtained by
the
Description property.
■Source Code The VehicleDescriptionAttributeReader project is included under the Chapter 16 subdirectory.
Reflecting on Attributes Using Late Binding
The previous example made use of early binding to print out the vehicle description data for the
Winnebago type. This was possible due to the fact that the VehicleDescriptionAttribute class type
was defined as a public member in the

AttributedCarLibrary assembly. It is also possible to make
use of dynamic loading and late binding to reflect over attributes.
Create a new project called VehicleDescriptionAttributeReaderLateBinding and copy
AttributedCarLibrary.dll to the project’s \bin\Debug directory. Now, update your Main() method
as follows:
using System.Reflection;
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 551
8849CH16.qxd 10/9/07 4:27 PM Page 551
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Descriptions of Your Vehicles *****\n");
// Load the local copy of AttributedCarLibrary.
Assembly asm = Assembly.Load("AttributedCarLibrary");
// Get type info of VehicleDescriptionAttribute.
Type vehicleDesc =
asm.GetType("AttributedCarLibrary.VehicleDescriptionAttribute");
// Get type info of the Description property.
PropertyInfo propDesc = vehicleDesc.GetProperty("Description");
// Get all types in the assembly.
Type[] types = asm.GetTypes();
// Iterate over each type and obtain any VehicleDescriptionAttributes.
foreach (Type t in types)
{
object[] objs = t.GetCustomAttributes(vehicleDesc, false);
// Iterate over each VehicleDescriptionAttribute and print
// the description using late binding.

foreach (object o in objs)
{
Console.WriteLine("-> {0}: {1}\n",
t.Name, propDesc.GetValue(o, null));
}
}
Console.ReadLine();
}
}
If you were able to follow along with the examples in this chapter, this Main() method should
be (more or less) self-explanatory. The only point of interest is the use of the
PropertyInfo.
GetValue()
method, which is used to trigger the property’s accessor. Figure 16-10 shows the
output.
Figure 16-10. Reflecting on attributes using late binding
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING552
8849CH16.qxd 10/9/07 4:27 PM Page 552
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
■Source Code The VehicleDescriptionAttributeReaderLateBinding project is included under the Chapter 16
subdirectory.
Putting Reflection, Late Binding, and Custom
Attributes in Perspective
Even though you have seen numerous examples of these techniques in action, you may still be
wondering when to make use of reflection, dynamic loading, late binding, and custom attributes in
your programs. To be sure, these topics can seem a bit on the academic side of programming (which
may or may not be a bad thing, depending on your point of view). To help map these topics to a
real-world situation, you need a solid example. Assume for the moment that you are on a program-
ming team that is building an application with the following requirement:

• The product must be extendable by the use of additional third-party tools.
So, what exactly is meant by
extendable? Consider the Visual Studio 2008 IDE. When this appli-
cation was developed, various “hooks” were inserted to allow other software vendors to snap
custom modules into the IDE. Obviously, the Visual Studio 2008 development team had no way to
set references to external .NET assemblies it had not developed yet (thus, no early binding), so how
exactly would an application provide the required hooks? Here is one possible way to solve this
problem:
• First, an extendable application must provide some input vehicle to allow the user to specify
the module to plug in (such as a dialog box or command-line flag). This requires
dynamic
loading
.
• Second, an extendable application must be able to determine whether the module supports
the correct functionality (such as a set of required interfaces) in order to be plugged into the
environment. This requires
reflection.
• Finally, an extendable application must obtain a reference to the required infrastructure
(such as a set of interface types) and invoke the members to trigger the underlying function-
ality. This may require
late binding.
Simply put, if the extendable application has been preprogrammed to query for specific inter-
faces, it is able to determine at runtime whether the type can be activated. Once this verification
test has been passed, the type in question may support additional interfaces that provide a poly-
morphic fabric to their functionality. This is the exact approach taken by the Visual Studio 2008
team, and despite what you may be thinking, is not at all difficult.
Building an Extendable Application
In the sections that follow, I will take you through a complete example that illustrates the process of
building an extendable Windows Forms application that can be augmented by the functionality of
external assemblies. What I will not do at this point is comment on the process of programming

Windows Forms applications (see Chapter 27 for an overview of the Windows Forms API). So, if you
are not familiar with the process of building Windows Forms applications, feel free to simply open
up the supplied sample code and follow along. To serve as a road map, our extendable application
entails the following assemblies:
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 553
8849CH16.qxd 10/9/07 4:27 PM Page 553
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
• CommonSnappableTypes.dll: This assembly contains type definitions that will be used by each
snap-in object and will be directly referenced by the Windows Forms application.

CSharpSnapIn.dll: A snap-in written in C#, which leverages the types of
CommonSnappableTypes.dll.

VbNetSnapIn.dll: A snap-in written in Visual Basic, which leverages the types of
CommonSnappableTypes.dll.

MyExtendableApp.exe: This Windows Forms application will be the entity that may be
extended by the functionality of each snap-in.
Again, this application will make use of dynamic loading, reflection, and late binding to
dynamically gain the functionality of assemblies it has no prior knowledge of.
Building CommonSnappableTypes.dll
The first order of business is to create an assembly that contains the types that a given snap-in must
leverage to be plugged into the expandable Windows Forms application. The CommonSnappable-
Types Class Library project defines two types:
namespace CommonSnappableTypes
{
public interface IAppFunctionality
{
void DoIt();

}
[AttributeUsage(AttributeTargets.Class)]
public sealed class CompanyInfoAttribute : System.Attribute
{
private string companyName;
private string companyUrl;
public CompanyInfoAttribute(){}
public string Name
{
get { return companyName; }
set { companyName = value; }
}
public string Url
{
get { return companyUrl; }
set { companyUrl = value; }
}
}
}
The IAppFunctionality interface provides a polymorphic interface for all snap-ins that can be
consumed by the extendable Windows Forms application. Given that this example is purely illustra-
tive, you supply a single method named
DoIt(). A more realistic interface (or a set of interfaces)
might allow the object to generate scripting code, render an image onto the application’s toolbox, or
integrate into the main menu of the hosting application.
The
CompanyInfoAttribute type is a custom attribute that will be applied on any class type
that wishes to be snapped in to the container. As you can tell by the definition of this class,
[CompanyInfo] allows the developer of the snap-in to provide some basic details about the compo-
nent’s point of origin.

CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING554
8849CH16.qxd 10/9/07 4:27 PM Page 554
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
Building the C# Snap-In
Next up, you need to create a type that implements the IAppFunctionality interface. Again, to focus
on the overall design of an extendable application, a trivial type is in order. Assume a new C# Class
Library project named CSharpSnapIn defines a class type named
CSharpModule. Given that this class
must make use of the types defined in CommonSnappableTypes, be sure to set a reference to this
binary (as well as
System.Windows.Forms.dll to display a noteworthy message). This being said,
here is the code:
using System;
using CommonSnappableTypes;
using System.Windows.Forms;
namespace CSharpSnapIn
{
[CompanyInfo(Name = "Intertech Training",
Url = "www.intertech.com")]
public class CSharpModule : IAppFunctionality
{
void IAppFunctionality.DoIt()
{
MessageBox.Show("You have just used the C# snap in!");
}
}
}
Notice that I choose to make use of explicit interface implementation when supporting the
IAppFunctionality interface. This is not required; however, the idea is that the only part of the

system that needs to directly interact with this interface type is the hosting Windows application.
By explicitly implementing this interface, the
DoIt() method is not directly exposed from the
CSharpModule type.
Building the Visual Basic Snap-In
Now, to simulate the role of a third-party vendor who prefers Visual Basic over C#, create a new
Visual Basic code library (VbNetSnapIn) that references the same external assemblies as the previ-
ous CSharpSnapIn project. The code is (again) intentionally simple:
Imports System.Windows.Forms
Imports CommonSnappableTypes
<CompanyInfo(Name:="Chucky's Software", Url:="www.ChuckySoft.com")> _
Public Class VbNetSnapIn
Implements IAppFunctionality
Public Sub DoIt() Implements CommonSnappableTypes.IAppFunctionality.DoIt
MessageBox.Show("You have just used the VB .NET snap in!")
End Sub
End Class
Notice that applying attributes in the syntax of Visual Basic requires angle brackets (< >) rather
than square brackets (
[ ]). Also notice that the Implements keyword is used to implement interface
types on a given class or structure.
CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 555
8849CH16.qxd 10/9/07 4:27 PM Page 555
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -
Building an Extendable Windows Forms Application
The final step is to create a new Windows Forms application (MyExtendableApp) that allows the
user to select a snap-in using a standard Windows Open dialog box. Next, set a reference to the
CommonSnappableTypes.dll assembly, but not the CSharpSnapIn.dll or VbNetSnapIn.dll code
libraries. Remember that the whole goal of this application is to make use of late binding and

reflection to determine the “snapability” of independent binaries created by third-party vendors.
Again, I won’t bother to examine all the details of Windows Forms development at this point
in the text. However, assuming you have placed a
MenuStrip component onto the forms designer,
define a topmost menu item named File that provides a single submenu named Snap In Module. As
well, the main window will contain a
ListBox type (which I renamed as lstLoadedSnapIns) that will
be used to display the names of each snap-in loaded by the user. Figure 16-11 shows the final GUI.
Figure 16-11. GUI for MyExtendableApp
The code that handles the Click event for the File ➤ Snap In Module menu item (which may
be created simply by double-clicking the menu item from the design-time editor) displays a File
Open dialog box and extracts the path to the selected file. Assuming the user did not select the
CommonSnappableTypes.dll assembly (as this is purely infrastructure), the path is then sent into a
helper function named
LoadExternalModule() for processing. This method will return false when
it is unable to find a class implementing
IAppFunctionality:
private void snapInModuleToolStripMenuItem_Click(object sender,
EventArgs e)
{
// Allow user to select an assembly to load.
OpenFileDialog dlg = new OpenFileDialog();
if (dlg.ShowDialog() == DialogResult.OK)
{
if(dlg.FileName.Contains("CommonSnappableTypes"))
MessageBox.Show("CommonSnappableTypes has no snap-ins!");
else if(!LoadExternalModule(dlg.FileName))
MessageBox.Show("Nothing implements IAppFunctionality!");
}
}

CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING556
8849CH16.qxd 10/9/07 4:27 PM Page 556
www.free-ebooks-download.org
Simpo PDF Merge and Split Unregistered Version -

×