Creating and Using Types with Reflection, Custom Attributes, the CodeDOM, and Lambda Expressions - MCSD Certification Toolkit (Exam 70-483): Programming in C# (2013)

MCSD Certification Toolkit (Exam 70-483): Programming in C# (2013)

Chapter 8

Creating and Using Types with Reflection, Custom Attributes, the CodeDOM, and Lambda Expressions

What You Will Learn in this Chapter

· Using the System.Reflection namespace

· Reading and creating custom attributes

· Generating code using the CodeDOM namespace

· Understanding lambda expressions

WROX.COM CODE DOWNLOADS FOR THIS CHAPTER

You can find the code downloads for this chapter at www.wrox.com/remtitle.cgi?isbn=1118612094 on the Download Code tab. The code is in the chapter08 download and individually named according to the names throughout the chapter.

This chapter has a mix of topics that will be covered on the exam, and these four topics do not directly relate to each other. The first three topics cover features that can be used to examine, customize, or generate C# code, while the last topic explains how to write shorthand syntax for your existing methods. It is especially important to understand the concept of lambda expressions because not only will you see them used extensively in Chapter 10, “Working with Language Integrated Query (LINQ),” but you will see questions on the test that use lambda expressions and you could be asked what the result of the expression would be.

Table 8-1 introduces you to the exam objectives covered in this chapter.

Table 8-1: 70-483 Exam Objectives Covered in This Chapter

Objective

Content Covered

Create and use types

Reflection. This includes finding, executing, and creating types at run time.
Attributes. This includes creating, applying, and reading attributes that can be used to change the behavior of your class.
The CodeDOM. This includes creating code generators.
Lambda expressions. This is shorthand syntax for creating methods without the normal method declaration syntax.

Using the System.Reflection Namespace

Reflection refers to the ability to examine code and dynamically read, modify, or invoke behavior for an assembly, module, or type. A type is any class, interface, array, value type, enumeration, parameter, generic type definition, and open or closed constructed generic types. You can use the classes in the System.Reflection namespace and the System.Type class to discover the assembly name, the namespace, the properties, the methods, the base class, and plenty of other metadata about a class or variable.

The System.Reflection namespace contains numerous classes that can be used to read metadata or dynamically invoke behavior from a type. Table 8-2 lists some of the frequently used classes in the System.Reflection namespace.

Table 8-2: Commonly Used Classes in the System.Reflection Namespace

Type

Description

Assembly

Represents a DLL or EXE file and contains properties for the Assembly name, classes, modules, and other metadata language run-time application.

EventInfo

Represents an event defined in your class and contains properties such as the event name.

FieldInfo

Represents a field defined in your class and contains properties such as whether the field is public or private.

MemberInfo

Abstracts the metadata about a class and can represent an event, a field, and so on.

MethodInfo

Represents a method defined in your class and can be used to invoke the method.

Module

The module is a file that composes the assembly. This is usually a DLL or EXE file.

ParameterInfo

Represents a parameter declaration for a method or a constructor. This allows you to determine the type of parameter, its name, as well as other properties.

PropertyInfo

Represents a property defined in your class and contains properties such as the property name and type.

Reflection is a powerful feature and can be used with some design patterns such as the Factory or Inversion of Control design patterns. These design patterns are more advanced topics and won’t be covered in the test, but it is important to understand the concept of reflection and its capabilities for the test.

Assembly Class

An assembly is essentially a compiled piece of code that is typically a DLL or EXE file. You can use the Assembly class to load the assembly, read metadata about the assembly, and even create instances of the types contained in the assembly. Table 8-3 lists frequently used properties for an Assembly.

Table 8-3: Commonly Used System.Reflection.Assembly Properties

Property

Description

CodeBase

Returns the path for the assembly

DefinedTypes

Returns a collection of the types defined in this assembly

ExportedTypes

Returns a collection of the public types defined in this assembly

FullName

Returns the name of the assembly

GlobalAssemblyCache

Returns a boolean value indicating whether the assembly was loaded from the global assembly cache

ImageRuntimeVersion

Returns the version of the Common Language Runtime (CLR) for the assembly

Location

Returns the path or UNC location of the assembly

Modules

Returns a collection that contains the modules in this assembly

SecurityRuleSet

Returns a value that indicates which set of security rules the Common Language Runtime enforces for the assembly

The following code sample loads the System.Data assembly and writes some of the assembly’s properties to the Output window:

Assembly myAssembly = Assembly.Load("System.Data, Version=4.0.0.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089");

Debug.WriteLine("CodeBase: {0}", myAssembly.CodeBase);

Debug.WriteLine("FullName: {0}", myAssembly.FullName);

Debug.WriteLine("GlobalAssemblyCache: {0}", myAssembly.GlobalAssemblyCache);

Debug.WriteLine("ImageRuntimeVersion: {0}", myAssembly.ImageRuntimeVersion);

Debug.WriteLine("Location: {0}", myAssembly.Location);

The preceding code produces the following output:

CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_32/System.Data/

v4.0_4.0.0.0__b77a5c561934e089/System.Data.dll

FullName: System.Data, Version=4.0.0.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089

GlobalAssemblyCache: True

ImageRuntimeVersion: v4.0.30319

Location: C:\Windows\Microsoft.Net\assembly\GAC_32\System.Data\

v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll

Table 8-4 lists the frequently used methods for an Assembly.

Table 8-4: Commonly Used System.Reflection.Assembly Methods

Method

Description

CreateInstance(String)

Creates an instance of the class by searching the assembly for the class name

GetCustomAttributes(Boolean)

Returns an array of objects that represent the custom attributes for this assembly

GetExecutingAssembly

Returns an Assembly object for the currently running program

GetExportedTypes

Returns the public classes defined in this assembly

GetModule

Returns the specified module in this assembly

GetModules()

Returns all the modules that are part of this assembly

GetName()

Returns the AssemblyName for this assembly

GetReferencedAssemblies

Returns an array of AssemblyName objects that represent all referenced assemblies

GetTypes

Returns an array of Type object defined in this assembly

Load(String)

Loads an assembly given the long form of its name

LoadFile(String)

Loads the contents of an assembly file given a file path

LoadFrom(String)

Loads an assembly given its file name or path

ReflectionOnlyLoad(String)

Loads an assembly but you can only perform reflection on the types defined in the assembly

UnsafeLoadFrom

Loads an assembly bypassing some security checks

The GetExecutingAssembly method is a static method that enables you to get a reference to the currently executing code. The GetExportedTypes and GetTypes methods are all used to get references to the types defined in the assembly. (The System.Type class will be explained in more detail in the next section.) The difference between GetExportedTypes and GetTypes is that the GetExportedTypes returns only the types that are public. The following code snippet displays all the types defined in the currently executing assembly:

Assembly myAssembly = Assembly.GetExecutingAssembly();

Type[] myAssemblysTypes = myAssembly.GetTypes();

foreach (Type myType in myAssemblysTypes)

{

Debug.WriteLine(string.Format("myType Name: {0}", myType.Name));

}

The Modules property or the GetLoadedModules, GetModules, and GetModule methods return the list of modules or a specific module defined in the assembly. A module is a file that composes the Assembly. This is usually a single DLL or EXE file. The following code lists all the modules defined in the System.Data assembly:

Assembly myAssembly = Assembly.Load("System.Data, Version=4.0.0.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089");

Module[] myAssemblysModules = myAssembly.GetModules();

foreach (Module myModule in myAssemblysModules)

{

Debug.WriteLine(string.Format("myModule Name: {0}", myModule.Name));

}

The output for the preceding code snippet is as follows:

myModule Name: System.Data.dll

The preceding code snippet used the Load method to load the System.Data assembly into memory. Because the assembly was loaded using the Load method, you can then execute code within the assembly. If you do not need to execute code, you can use the ReflectionOnlyLoad method.

You can also load an assembly by calling the LoadFrom or LoadFile methods. Both methods take a file path as a parameter. To understand the difference between LoadFrom and LoadFile, you need to understand the concept of context and the different types of context there are. Think of the context as where reflection searches for the assembly. An assembly can be in one of three contexts or no context at all:

· The first context is the load context, which contains the assemblies found by probing. Probing is the process of looking in the GAC, the host assembly store, the folder of the executing assembly, or the private bin folder of the executing assembly to find the assembly.

· The second context is the load-from context. This contains the assemblies located in the path passed into the LoadFrom method.

· The third context is the reflection-only context, which contains the assemblies loaded with the ReflectionOnlyLoad and ReflectionOnlyLoadFrom methods.

ADVICE FROM THE EXPERTS: The Disadvantages of Using LoadFrom

There are a few disadvantages of using the LoadFrom method, and using Load is recommended. First, if there is already an assembly loaded with the same identity, then this assembly is returned, not the one found in the file path. If an assembly of the same identity is found in the probing path but in a different location, an exception can occur. Third, you must have FileIOPermissionAccess.Read and FileIOPermissionAccess.PathDiscovery permissions to the file path. LoadFile is different in that it can load the specific file passed in the parameter, but you still need the correct file permissions to load the assembly. You would use the LoadFile when there are two assemblies with the same identity in different folders on the computer.

Once you have an assembly loaded you can then create instances of the classes defined in the assembly. To create an instance of a class, use the CreateInstance method. The following code creates an instance of the DataTable and prints the number of rows to the Output window:

Assembly myAssembly = Assembly.Load("System.Data, Version=4.0.0.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089");

DataTable dt = (DataTable)myAssembly.CreateInstance("System.Data.DataTable");

Debug.Print("Number of rows: {0}", dt.Rows.Count);

COMMON MISTAKES: Working with the CreateInstance Method

The CreateInstance method will not throw an exception if you pass in a type name that is not found in the assembly. The return value will be null, so be sure to check that the name is correct.

The GetReferencesAssemblies is used to discover the references for the assembly. This can be helpful when troubleshooting deployment issues. The following code prints all the referenced assemblies for the System.Data assembly:

Assembly myAssembly = Assembly.Load("System.Data, Version=4.0.0.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089");

AssemblyName[] referencedAssemblyNames = myAssembly.GetReferencedAssemblies();

foreach (AssemblyName assemblyName in referencedAssemblyNames)

{

Debug.WriteLine("Assembly Name: {0}", assemblyName.Name);

Debug.WriteLine("Assembly Version: {0}", assemblyName.Version);

}

The preceding code will produce the following results:

Assembly Name: mscorlib

Assembly Version: 4.0.0.0

Assembly Name: System

Assembly Version: 4.0.0.0

Assembly Name: System.Xml

Assembly Version: 4.0.0.0

Assembly Name: System.Configuration

Assembly Version: 4.0.0.0

Assembly Name: System.Transactions

Assembly Version: 4.0.0.0

Assembly Name: System.Numerics

Assembly Version: 4.0.0.0

Assembly Name: System.EnterpriseServices

Assembly Version: 4.0.0.0

Assembly Name: System.Core

Assembly Version: 4.0.0.0

The GetCustomAttributes and GetCustomAttributesData will be explained later in this chapter in the “Read and Create Custom Attributes” section. But for now just realize that you can use the Assembly class to get the list of custom attribute classes defined in an assembly.

The System.Type Class

The System.Type class represents a class, interface, array, value type, enumeration, parameter, generic type definitions, and open or closed constructed generic types. For the most part, you usually use a Type to get information about a class contained in an assembly. You can obtain a reference to a type in two ways. You can use the typeof() keyword and pass in the name of the type:

System.Type myType = typeof(int);

Or you can use the GetType() method on an instance of the type:

int myIntVariable = 0;

System.Type myType = myIntVariable.GetType();

Both of these examples create an instance of a type class for the int type. After you have a reference to the type, you can then examine the properties. Table 8-5 lists commonly used properties for the System.Type class.

Table 8-5: Commonly Used Properties of the System.Type Class

Property

Description

Assembly

Returns the Assembly in which the type is declared

AssemblyQualifiedName

Returns a string which is the assembly-qualified name of the Type, which includes the name of the assembly from which the Type was loaded

BaseType

Return a Type from which the current Type inherits

FullName

Returns a string which is the fully qualified name of the Type, including the namespace of the Type but not the assembly

IsAbstract

Returns a boolean value indicating whether the Type is abstract

IsArry

Returns a boolean value indicating whether the Type is an array

IsClass

Returns a boolean value indicating whether the Type is a class rather than a value type or interface

IsEnum

Returns a boolean value indicating whether the current Type represents an enumeration

IsInterface

Returns a boolean value indicating whether the Type is an interface

IsNotPublic

Returns a boolean value indicating whether the Type is not declared public

IsPublic

Returns a boolean value indicating whether the Type is declared as public

IsSerializable

Returns a boolean value indicating whether the Type is serializable

IsValueType

Returns a boolean value indicating whether the Type is a value type

Name

Returns the name of the type

Namespace

Returns the namespace of the current type

The following code creates an instance of an int variable, obtains a reference to the int type, and writes some of its properties to the Output window:

int myIntVariable = 0;

System.Type myType = myIntVariable.GetType();

Debug.WriteLine("AssmeblyQualifiedName: {0}", myType.AssemblyQualifiedName);

Debug.WriteLine("FullName: {0}", myType.FullName);

Debug.WriteLine("IsValueType: {0}", myType.IsValueType);

Debug.WriteLine("Name: {0}", myType.Name);

Debug.WriteLine("Namespace: {0}", myType.Namespace);

The preceding code produces the following output:

AssmeblyQualifiedName: System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089

FullName: System.Int32

IsValueType: True

Name: Int32

Namespace: System

The System.Type class also has methods that you can use to get the properties, methods, constructors, interfaces, events and any other metadata about the type. Table 8-6 lists some of the methods of the type class.

Table 8-6: Commonly Used Methods of the System.Type Class

Name

Description

GetArrayRank

When the type represents an array, this returns the number of dimensions in an Array.

GetConstructor(Type[])

Searches for a public instance constructor whose parameters match the types in the specified array and returns a ConstructorInfo object.

GetConstructors()

Returns an array of ConstructorInfo objects for all the public constructors defined for the current Type.

GetEnumName

When the type represents an enumeration, this returns the name of the element that has the specified value.

GetEnumNames

When the type represents an enumeration, this returns the all of the names of the members.

GetEnumValues

When the type represents an enumeration, this returns an array of the values of the enumeration.

GetField(String)

Searches for the public field with the specified name.

GetFields()

Returns all the public fields of the current Type.

GetInterface(String)

Returns the interface with the specified name.

GetMember(String)

Returns the public member with the specified name.

GetMembers()

Returns all public members of the current Type.

GetMethod(String)

Returns the public method with the specified name.

GetMethods()

Returns all the public methods of the current Type.

GetProperties()

Returns all the public properties of the current Type.

GetProperty(String)

Returns the public property with the specified name.

GetTypeArray

Returns all the types of objects in the specified array.

InvokeMember(String, BindingFlags, Binder, Object, Object[])

Executes a method using the specified binding constraints and matching the specified argument list.

GetArrayRank

When the Type object represents an array, the GetArrayRank method returns the number of dimensions in the array. The following code creates a three-dimensional array and prints the array rank to the Output window:

int[,,] myIntArray = new int[5,6,7];

Type myIntArrayType = myIntArray.GetType();

Debug.Print("Array Rank: {0}", myIntArrayType.GetArrayRank());

The preceding code prints the following to the Output window:

Array Rank: 3

GetConstructors

The GetConstructors method returns an array of ConstructorInfo objects that you can use to get information about all the constructors of the type. The following code prints the constructors and the parameters for a System.DataTable object to the Output window:

DataTable myDataTable = new DataTable();

Type myDataTableType = myDataTable.GetType();

ConstructorInfo[] myDataTableConstructors = myDataTableType.GetConstructors();

for(int i = 0; i <= myDataTableConstructors.Length - 1; i++)

{

ConstructorInfo constructorInfo = myDataTableConstructors[i];

Debug.Print("\nConstructor #{0}", i + 1);

ParameterInfo[] parameters = constructorInfo.GetParameters();

Debug.Print("Number Of Parameters: {0}", parameters.Length);

foreach (ParameterInfo parameter in parameters)

{

Debug.Print("Parameter Name: {0}", parameter.Name);

Debug.Print("Parameter Type: {0}",

parameter.ParameterType.Name);

}

}

The preceding code produces the following output:

Constructor #1

Number Of Parameters: 0

Constructor #2

Number Of Parameters: 1

Parameter Name: tableName

Parameter Type: String

Constructor #3

Number Of Parameters: 2

Parameter Name: tableName

Parameter Type: String

Parameter Name: tableNamespace

Parameter Type: String

GetEnumName, GetEnumNames, and GetEnumValues

When the Type object represents an enumeration, the GetEnum methods enable you to determine all the names and values within an enumeration. For example, the following is a custom enumeration with three members:

private enum MyCustomEnum

{

Red = 1,

White = 2,

Blue = 3

}

The following code writes all the names in the enumeration to the Output window:

Type myCustomEnumType = typeof(MyCustomEnum);

string[] enumNames = myCustomEnumType.GetEnumNames();

foreach (string enumName in enumNames)

{

Debug.Print(string.Format("Name: {0}", enumName));

}

The preceding code displays the following in the Output window:

Name: Red

Name: White

Name: Blue

You can obtain the values for the enumeration by using the GetEnumValues method. The following code prints all the enumerations values to the Output window:

Type myCustomEnumType = typeof(MyCustomEnum);

Array enumValues = myCustomEnumType.GetEnumValues();

foreach (object enumValue in enumValues)

{

Debug.Print(string.Format("Enum Value: {0}", enumValue.ToString()));

}

The preceding code produces the following output:

Enum Value: Red

Enum Value: White

Enum Value: Blue

COMMON MISTAKES: The Value Is the Same as the Name

Note that the value is the same as the name, not the numbers 1, 2, or 3.

You can also use the GetEnumName method to retrieve the name. The following displays all the members by using the underlying value of the enumeration.

Type myCustomEnumType = typeof(MyCustomEnum);

for (int i = 1; i <= 3; i++)

{

string enumName = myCustomEnumType.GetEnumName(i);

Debug.Print(string.Format("{0}: {1}", enumName, i));

}

The preceding code produces the following output.

Red: 1

White: 2

Blue: 3

GetField and GetFields

A field is a variable defined in a class or struct. The GetField method is used to get a FieldInfo object for one field. The GetFields method returns an array of FieldInfo objects. The GetFields method can also return the fields from inherited classes. When you call GetFields, you pass in theBindingFlags enumeration to specify the scope of fields that you want it to return. For example, the following class contains five fields all with different scope:

class ReflectionExample

{

private string _privateField = "Hello";

public string _publicField = "Goodbye";

internal string _internalfield = "Hola";

protected string _protectedField = "Adios";

static string _staticField = "Bonjour";

}

You can use GetFields to get the values of these variables regardless of the scope.

ReflectionExample reflectionExample = new ReflectionExample();

Type reflectionExampleType = typeof(ReflectionExample);

FieldInfo[] fields = reflectionExampleType.GetFields(BindingFlags.Public |

BindingFlags.Instance |

BindingFlags.Static |

BindingFlags.NonPublic |

BindingFlags.FlattenHierarchy);

foreach (FieldInfo field in fields)

{

object fieldValue = field.GetValue(reflectionExample);

Debug.WriteLine(string.Format("Field Name: {0}, Value: {1}", field.Name,

fieldValue.ToString()));

}

The preceding code produces the following output:

Field Name: _privateField, Value: Hello

Field Name: _publicField, Value: Goodbye

Field Name: _internalfield, Value: Hola

Field Name: _protectedField, Value: Adios

Field Name: _staticField, Value: Bonjour

When calling GetFields, you use the BindingFlags enumeration and can specify more than one value by using the bitwise operator.

ADVICE FROM THE EXPERTS: Declaring Variables as Private

Even if you declare a variable as private, the user of your class can still read the value of the field using reflection, so be careful what you put in these variables, especially if you store passwords or sensitive data.

The FieldInfo object also has a SetValue method that enables you to change the value of the field, even if it is private or protected. To demonstrate, add the following get accessor to the ReflectionExample class:

public string PrivateField

{

get { return privateField; }

}

The following code changes the value of the privateField variable and prints its value to the Output window:

ReflectionExample reflectionExample = new ReflectionExample();

Type reflectionExampleType = typeof(ReflectionExample);

reflectionExampleType.GetField("privateField", BindingFlags.NonPublic |

BindingFlags.Instance).SetValue(reflectionExample, "My New Value");

Debug.Print("Private Field Value: {0}",

reflectionExample.PrivateField);

The preceding code produces the following output:

Private Field Value: My New Value

GetProperty and GetProperties

The GetProperty and GetProperties methods are similar to the GetField and GetFields methods because they enable you to get the properties, get their value, or set their value. The difference is that you use a PropertyInfo object instead of the FieldInfo object, and you can access only properties instead of fields. The PropertyInfo object has GetValue and SetValue methods just like the FieldInfo object and works the same way.

GetMethod and GetMethods

The GetMethod and GetMethods methods enable you to obtain information about a method for a type. After you have a reference to the method, you can execute the method by calling the Invoke method of the MethodInfo class. You can also execute the method by calling the InvokeMember method of the Sytem.Type class and pass in the name of the method and its parameters.

Add the following method to the ReflectionExample class:

public double Multiply(double x, double y)

{

return x * y;

}

The following code calls the Multiply method and prints the return value to the Output window:

ReflectionExample reflectionExample = new ReflectionExample();

Type reflectionExampleType = typeof(ReflectionExample);

MethodInfo methodInfo = reflectionExampleType.GetMethod("Multiply");

double returnValue = (double)methodInfo.Invoke(reflectionExample,

new object[] { 4, 5 });

Debug.Print("Return Value: {0}", returnValue);

Notice that you pass parameters to the method by creating an array of objects and passing it as the second parameter to the Invoke method. The preceding code prints “Return Value: 20” to the Output window.

When using the InvokeMember method of the System.Type class, the syntax is as follows:

ReflectionExample reflectionExample = new ReflectionExample();

Type reflectionExampleType = typeof(ReflectionExample);

double returnValue = (double)reflectionExampleType.InvokeMember("Multiply",

BindingFlags.InvokeMethod,

null,

reflectionExample,

new object[] { 4, 5 });

Debug.Print(string.Format("Return Value: {0}", returnValue));

The second parameter is BindingFlags.InvokeMethod, which triggers the InvokeMember method to invoke the method.

REAL-WORLD CASE SCENARIO: Using reflection to map a table’s columns to the properties of a class

A common pattern in a C# application is to map the columns in a table to the properties of a class. You can use reflection to create a shared utility method that can set the properties of your class to the value of the columns in your table and only have to write this code once for all your classes. This can save you a lot of time. For this exercise, you will need a database with a table called Person and a class that already contains a property for each column. Figure 8-1 displays the design of the table in SQL Server Enterprise Manager.

Figure 8-1: Table design in SQL Server Enterprise Manager

image

The following code is your class definition:

class Person

{

public int PersonId { get; set; }

public string FirstName { get; set; }

public string LastName { get; set; }

public string Address { get; set; }

public string City { get; set; }

public string State { get; set; }

public string ZipCode { get; set; }

}

Solution

1. Create a class called ReflectionExample and add the following method:

public static bool LoadClassFromSQLDataReader(object myClass, SqlDataReader dr)

{

if (dr.HasRows)

{

dr.Read();

Type typeOfClass = myClass.GetType();

for (int columnIndex = 0; columnIndex <= dr.FieldCount - 1; columnIndex++)

{

//Get the name of the column

string columnName = dr.GetName(columnIndex);

//Check if a property exists that matches that name.

PropertyInfo propertyInfo = typeOfClass.GetProperty(columnName);

if (propertyInfo != null)

{

//Set the value to the value in the SqlDataReader

propertyInfo.SetValue(myClass, dr.GetValue(columnIndex));

}

}

return true;

}

else

{

return false;

}

}

This method takes an instance of a class and a SqlDataReader as a parameter. It first checks if the SqlDataReader has rows, and if it does it positions the cursor on the first record. Then it gets the type for myClass using the GetType method. Next, it loops around for each column in theSqlDataReader. In the loop it first gets the column name and then tries to retrieve the property from the object with that name. If the property exists, the value of the property is set to the data in the column.

2. Now you can add the GetPerson method to the Person class to use this method:

public bool GetPerson(int personId)

{

//Open the connection to the database.

SqlConnection cn = new

SqlConnection("Server=(local);Database=Reflection;Trusted_Connection=True;");

cn.Open();

//Retrieve the record.

SqlCommand cmd = new SqlCommand(

string.Format("SELECT * FROM Person WHERE PersonId = {0}", personId), cn);

SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);

return ReflectionExample.LoadClassFromSQLDataReader(this, dr);

}

Be aware, though, that when using reflection there is a performance hit because there is extra processing going on to resolve all the references. You need to take this into account when deciding whether to use reflection.

Read and Create Custom Attributes

Attributes enable you to define metadata for a class, a property, or a method. The class, property, or method is referred to as the target of the attribute. Reflection can then be used to read these attributes dynamically and change how the target behaves. Attributes are contained in square brackets “[ ]” above the target and can be stacked on top of each other when multiple attributes are needed to define the target. For example, if you want to make a class serializable, you need to add the [Serializable()] attribute above the class declaration:

[Serializable()]

public class MyClass

{

}

The Serializable attribute is actually a class defined in the .NET Framework that inherits from System.Attribute. The System.Attribute class is an abstract class that is the base class for all custom attributes. This section explains how to read attributes using reflection and then create your own custom attributes.

Read Attributes

In the previous section you learned about the System.Reflection namespace and the numerous classes defined in the namespace that let you read the metadata about the classes within an assembly. An assembly has a GetCustomAttributes method that enables you to enumerate through all the custom attributes classes contained in the assembly or filter the specific type of attribute you would like to retrieve. The following code block iterates through all the referenced assemblies for the currently executing assembly and prints the custom attributes class names and properties to the Output window:

Assembly assembly = Assembly.GetExecutingAssembly();

AssemblyName[] assemblyNames = assembly.GetReferencedAssemblies();

foreach (AssemblyName assemblyName in assemblyNames)

{

Debug.WriteLine("\nAssembly Name: {0}", assemblyName.FullName);

Assembly referencedAssembly = Assembly.Load(assemblyName.FullName);

object[] attributes = referencedAssembly.GetCustomAttributes(false);

foreach (object attribute in attributes)

{

Debug.WriteLine(\nAttribute Name: {0}",

attribute.GetType().Name);

//Get the properties of this attribute

PropertyInfo[] properties = attribute.GetType().GetProperties();

foreach (PropertyInfo property in properties)

{

Debug.WriteLine("{0} : {1}", property.Name,

property.GetValue(attribute));

}

}

}

The following text is a partial list of the output from the preceding code. The currently executing assembly referenced three assemblies: mscorlib, System.Data, and System. Each has their own set of custom attribute classes.

Assembly Name: mscorlib, Version=4.0.0.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089

Attribute Name: StringFreezingAttribute

TypeId : System.Runtime.CompilerServices.StringFreezingAttribute

Assembly Name: System.Data, Version=4.0.0.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089

Attribute Name: AllowPartiallyTrustedCallersAttribute

PartialTrustVisibilityLevel : VisibleToAllHosts

TypeId : System.Security.AllowPartiallyTrustedCallersAttribute

Attribute Name: CLSCompliantAttribute

IsCompliant : True

TypeId : System.CLSCompliantAttribute

Attribute Name: RuntimeCompatibilityAttribute

WrapNonExceptionThrows : True

TypeId : System.Runtime.CompilerServices.RuntimeCompatibilityAttribute

Assembly Name: System, Version=4.0.0.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089

Attribute Name: ComVisibleAttribute

Value : False

TypeId : System.Runtime.InteropServices.ComVisibleAttribute

Notice that the GetCustomAttributes method returns an array of objects. This is because each attribute is its own class. The first attribute in the mscorlib assembly is the StringFreezingAttribute. This is a class that inherits from System.Attribute and can have its own properties and methods. As you read through the preceding output, you can see that each attribute class has its own custom properties and also the properties of the System.Attribute class.

Create Attributes

To create your own custom attribute, you need to create a class that inherits from the System.Attribute abstract class:

class MyCustomAttribute : System.Attribute

{

}

BEST PRACTICES: Naming Custom Attribute Classes

When naming a custom attribute class, it should always have Attribute at the end of its name. When using the custom attribute, you reference it by the name and exclude the Attribute part.

After you declare your class, you can then add properties and enumerations to the class just like any other class. For this example, you add one enumeration and three properties:

public enum MyCustomAttributeEnum

{

Red,

White,

Blue

}

public bool Property1 { get; set; }

public string Property2 { get; set; }

public MyCustomAttributeEnum Property3 { get; set; }

The next step is to define the scope of the attribute. For this example you limit the scope of this attribute so that the target can be only a class or a struct. To do this you use the System.AttributeUsage custom attribute. This attribute is applied to the class:

[System.AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]

class MyCustomAttribute : System.Attribute

{

}

Now you can use this attribute when defining a class. Create a new class named MyTestClass. Above the class declaration add the following code:

[MyCustom(Property1 = true, Property2 = "Hello World", Property3 =

MyCustomAttribute.MyCustomAttributeEnum.Red)]

class MyTestClass()

{

}

Notice that the name of the attribute is MyCustom, not MyCustomAttribute. To set the properties of the attribute, you pass the values to the constructor as named parameters. You could have also added a constructor to the MyCustomAttribute class with three parameters and then set the property values in the body of the constructor.

Now that you have the class with a custom attribute, you can use reflection to read the attribute values. The following code can read the custom attributes for the MyTestClass:

Type myTestClassType = typeof(MyTestClass);

MyCustomAttribute attribute =

(MyCustomAttribute)myTestClassType.GetCustomAttribute

(

typeof(MyCustomAttribute),

false

);

Debug.WriteLine("Property1: {0}", attribute.Property1);

Debug.WriteLine("Property2: {0}", attribute.Property2);

Debug.WriteLine("Property3: {0}", attribute.Property3);

The preceding code produces the following output:

Property1: True

Property2: Hello World

Property3: Red

REAL-WORLD CASE SCENARIO: Using custom attributes

In the previous Real-World Case Scenario, you learned how to use reflection to map the value of the columns in a table to the properties in a class. This works great as long as the names of the columns in the table match the names of the properties in the class. But if for some reason they do not match, you may want to map a column to a property of a different name. A custom attribute is a great way to handle this situation. Try creating it now, before looking at the solution provided.

Solution

1. Add a class to the project and name it DataMappingAttribute. Because this attribute should be used only for classes, set the AttributeTarget property to Class. You also want to use this attribute for more than one column, so you need to set the AllowMultiple property of theAttributeUsage class to true.

[System.AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]

class DataMappingAttribute : System.Attribute

{

}

2. Add two string properties called ColumnName and PropertyName. When implemented, these properties contain the column name that maps to the property name:

public string ColumnName { get; set; }

public string PropertyName { get; set; }

3. Add a constructor to the attribute class that takes the column and property names as parameters and sets the property value:

public DataMappingAttribute(string columnName, string propertyName)

{

ColumnName = columnName;

PropertyName = propertyName;

}

4. Open the Person class that was created in the previous Real-World Case Scenario, change the name of the FirstName property to FName, and change the LastName property to LName. Add the DataMapping attributes above the class name, and set the appropriate mappings for the first and last name columns\properties:

[DataMapping("FirstName", "FName")]

[DataMapping("LastName", "LName")]

class Person

{

}

5. Open the ReflectionExample class, and modify the LoadClassFromSqlDataReader method to the following:

public static bool LoadClassFromSQLDataReader(object myClass, SqlDataReader dr)

{

if (dr.HasRows)

{

dr.Read();

Type typeOfClass = myClass.GetType();

object[] dataMappingAttributes =

typeOfClass.GetCustomAttributes(typeof(DataMappingAttribute), false);

for (int columnIndex = 0; columnIndex <= dr.FieldCount - 1; columnIndex++)

{

//Get the name of the column.

string columnName = dr.GetName(columnIndex);

//Check if a property exists that matches that name.

PropertyInfo propertyInfo = null;

//Check if an attribute exists that maps this column to a property.

foreach (DataMappingAttribute dataMappingAttribute in

dataMappingAttributes)

{

if (dataMappingAttribute.ColumnName == columnName)

{

propertyInfo =

typeOfClass.GetProperty(dataMappingAttribute.PropertyName);

break;

}

}

//The the property was mapped explicitely then try to find a

//property with the same name as the column.

if (propertyInfo == null)

{

propertyInfo = typeOfClass.GetProperty(columnName);

}

//If you found a property then set its value.

if (propertyInfo != null)

{

//Set the value to the value in the SqlDataReader

propertyInfo.SetValue(myClass, dr.GetValue(columnIndex));

}

}

return true;

}

else

{

return false;

}

}

You can now see the power of attributes and how they can help control the flow of your application.

Generate Code Using the CodeDOM Namespace

The Code Document Object Model, CodeDOM, is a set of classes in the .NET Framework that enables you to create code generators. A code generator is a program that can write your code for you. The CodeDOM is an object model that contains classes that represent properties, methods, classes, boolean logic, parameters, and any other type of code element you can write in your program. You can use the CodeDOM classes to write your code generically and then have it generated in C#, VB.NET, or JScript.

Have you ever written classes that have properties that map to a table? This can be tedious and time-consuming. This is a great example of when you would want to use the CodeDOM to generate this code for you automatically. You can create a program that reads the columns from a table in a database and creates a class that contains a column for each property. This section explains the classes within the CodeDOM namespace and shows you how to generate code for classes, properties, and methods, and even generate looping structures and if statements.

Generate Code Using the CodeDOM Namespace

The classes in the CodeDOM model the code statements, such as an if statement or variable declaration. You can write a code generator using the CodeDOM classes and then generate your code in either C#, VB.NET, or JScript. The CodeDOM classes are great for automating repetitive coding tasks or enforcing patterns within your projects. This section will demonstrate how to create a class file using the CodeDOM that contains fields, properties, and methods.

The typical structure of a class within C# contains the following elements:

· Text file that contains the code for the class

· Set of using statements

· Namespace declaration

· Class name declaration

· Set of fields and properties

· Set of methods, which contain logic with looping structures and logical expressions such as if and switch statements

The CodeDOM namespace contains classes that enable you to create a structure called a CodeDOM Graph that models these elements. Table 8-7 lists some of the classes you can use in the CodeDOM namespace.

Table 8-7: Commonly Used Classes in the CodeDOM Namespace

Name

Description

CodeArgumentReferenceExpression

Represents a reference to the value of an argument passed to a method

CodeAssignStatement

Represents an assignment statement

CodeBinaryOperatorExpression

Represents an expression that consists of a binary operation

CodeCastExpression

Represents an expression that casts to another data type or interface

CodeComment

Represents a comment

CodeCompileUnit

Provides a container for a CodeDOM, which contains the declarations, namespace, class, and components of your class

CodeConditionStatement

Represents a conditional statement, typically represented as an if statement

CodeConstructor

Represents a declaration of an instance constructor

CodeFieldReferenceExpression

Represents a reference to a field

CodeIterationStatement

Represents a for statement or other looping structure

CodeMemberEvent

Represents a declaration for an event

CodeMemberField

Represents a declaration for a field

CodeMemberMethod

Represents a declaration for a method

CodeMemberProperty

Represents a declaration for a property

CodeMethodInvokeExpression

Represents an expression that invokes a method

CodeMethodReturnStatement

Represents a return value statement

CodeNamespace

Represents a namespace declaration

CodeNamespaceImport

Represents a namespace import directive

CodeObjectCreateExpression

Represents an expression that creates a new instance of a type

CodeParameterDeclarationExpression

Represents a parameter declaration for a method, property, or constructor

CodePropertyReferenceExpression

Represents a reference to the value of a property

CodePropertySetValueReferenceExpression

Represents the value argument of a property set method

CodeRegionDirective

Specifies the name and mode for a code region

CodeSnippetCompileUnit

Represents a literal code fragment that can be compiled

CodeSnippetStatement

Represents a statement using a literal code fragment

CodeThisReferenceExpression

Represents a reference to the current local class instance

CodeThrowExceptionStatement

Represents a statement that throws an exception

CodeTryCatchFinallyStatement

Represents a try, catch, and finally block

CodeTypeConstructor

Represents a static constructor for a class

CodeTypeDeclaration

Represents a declaration for a class, structure, interface, or enumeration

CodeTypeDelegate

Represents a delegate declaration

CodeVariableDeclarationStatement

Represents a variable declaration

CodeVariableReferenceExpression

Represents a reference to a local variable

As you can see from the list of classes in Table 8-6, the CodeDOM has classes for every type of statement you can make in the .NET Framework. You can then choose to have the code rendered in the language of your choice. For example, look at the following class and then later in the chapter you will use the CodeDOM to generate this class:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace Reflection

{

class Calculator

{

private double x;

private double y;

public double X

{

get { return this.x; }

set { this.x = value; }

}

public double Y

{

get { return this.y; }

set { this.y = value; }

}

public double Divide()

{

if (this.Y == 0)

{

return 0;

}

else

{

return this.X / this.Y;

}

}

public double Exponent(double power)

{

return Math.Pow(this.X, power);

}

}

}

This is a simple class called Calculator that is in the Reflection namespace, contains two fields, two properties, and two methods. The following sections demonstrates which classes in the CodeDOM you should use to create the class dynamically.

CodeCompileUnit

The CodeCompileUnit class is the top-level class that is the container for all other objects within the class you want to generate. Think of this as the class that represents the file that contains your code. The following code is used to create an instance of the CodeCompileUnit class:

CodeCompileUnit codeCompileUnit = new CodeCompileUnit();

CodeNamespace and CodeNamespaceImport

The next step is to add the namespace. The CodeNamspace class is used to represent the namespace. The constructor takes the namespace as the parameter.

CodeNamespace codeNamespace = new CodeNamespace("Reflection");

Now that you have a namespace, you can append the using statements. Normally, when you create a class file, the using statements are above the namespace declaration, but they still work if you add them after the namespace. The CodeNamespaceImport class is used to define the namespace you would like to import. In C# you use the using keyword, but in VB.NET you would use the imports keyword. By using the CodeDOM, you don’t have to worry about the correct keyword.

codeNamespace.Imports.Add(new CodeNamespaceImport("System"));

codeNamespace.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));

codeNamespace.Imports.Add(new CodeNamespaceImport("System.Linq"));

codeNamespace.Imports.Add(new CodeNamespaceImport("System.Text"));

codeNamespace.Imports.Add(new CodeNamespaceImport("System.Threading.Tasks"));

The preceding code produces the following output:

namespace Reflection

{

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

}

CodeTypeDeclaration

The next step is to declare the class. This is done by using the CodeTypeDeclaration class.

CodeTypeDeclaration targetClass = new CodeTypeDeclaration("Calculator");

targetClass.IsClass = true;

targetClass.TypeAttributes = TypeAttributes.Public;

//Add the class to the namespace.

codeNamespace.Types.Add(targetClass);

The preceding code creates an instance of the CodeTypeDeclaration class and sets the IsClass attribute to true, which tells the .NET Framework to generate a class declaration. The TypeAttributes property enables you to define attributes such as public, private, and static. These can be combined using the bitwise operator (|). After the class is defined, you need to add it to the Types collection of the namespace. The preceding code produces the following output:

public class Calculator

{

}

CodeMemberField

The next step is to add the fields to the class. This is done by using the CodeMemberField class. You simply create an instance of the class and set its Name property, set the Type property, and add it to the Members collection of the CodeTypeDeclaration object. The following code creates two fields, _xand _y, both of which are declared as a double:

CodeMemberField xField = new CodeMemberField();

xField.Name = "x";

xField.Type = new CodeTypeReference(typeof(double));

targetClass.Members.Add(xField);

CodeMemberField yField = new CodeMemberField();

yField.Name = "y";

yField.Type = new CodeTypeReference(typeof(double));

targetClass.Members.Add(yField);

The preceding code produces the following output:

private double x;

private double y;

CodeMemberProperty

The next step is to create the properties for the x and y fields. You use a CodeMemberProperty class to create a property and generate the get and set methods. The following code creates the X and Y properties in the Calculator class:

//X Property

CodeMemberProperty xProperty = new CodeMemberProperty();

xProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;

xProperty.Name = "X";

xProperty.HasGet = true;

xProperty.HasSet = true;

xProperty.Type = new CodeTypeReference(typeof(System.Double));

xProperty.GetStatements.Add(new CodeMethodReturnStatement(

new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "x")));

xProperty.SetStatements.Add(new CodeAssignStatement(

new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "x"),

new CodePropertySetValueReferenceExpression()));

targetClass.Members.Add(xProperty);

//Y Property

CodeMemberProperty yProperty = new CodeMemberProperty();

yProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;

yProperty.Name = "Y";

yProperty.HasGet = true;

yProperty.HasSet = true;

yProperty.Type = new CodeTypeReference(typeof(System.Double));

yProperty.GetStatements.Add(new CodeMethodReturnStatement(

new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "y")));

yProperty.SetStatements.Add(new CodeAssignStatement(

new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "y"),

new CodePropertySetValueReferenceExpression()));

targetClass.Members.Add(yProperty);

The CodeMemberProperty class has two properties (HasGet and HasSet) that you need to set to true so the code generator can create the Get and Set accessors. The GetStatements collection property is used to add the code to the Get accessor. In this example, the Get method returns the this.xfield. The CodeThisReferenceExpression class is used because in C# you use this; in VB you use Me. The code generator knows which keyword to use when you generate the code. The SetStatements collection property contains the code to set the this.x field. In this instance you need to create aCodeAssignStatement along with the CodePropertySetValueReferenceExpression. The preceding code produces the following output:

public double X

{

get

{

return this.x;

}

set

{

this.x = value;

}

}

public double Y

{

get

{

return this.y;

}

set

{

this.y = value;

}

}

CodeMemberMethod

The next step is to create the Divide method. To create methods using the CodeDOM, you need to use the CodeMemberMethod class. The following code creates an instance of the CodeMemberMethod class, names the method Divide, sets the return type to double, and sets its attributes to public and final. If you want to set other attributes, such as static, virtual, or new, you can use the bitwise operator to concatenate the attributes.

CodeMemberMethod divideMethod = new CodeMemberMethod();

divideMethod.Name = "Divide";

divideMethod.ReturnType = new CodeTypeReference(typeof(double));

divideMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;

Now that the method signature is defined, you need to create the code for the body of the method. The Divide method checks if the Y property is 0 and either returns 0 or the quotient. If logic is created by using the CodeConditonStatement class.

CodeConditionStatement ifLogic = new CodeConditionStatement();

ifLogic.Condition = new CodeBinaryOperatorExpression(

new CodeFieldReferenceExpression(

new CodeThisReferenceExpression(), yProperty.Name),

CodeBinaryOperatorType.ValueEquality,

new CodePrimitiveExpression(0));

ifLogic.TrueStatements.Add(new CodeMethodReturnStatement(

new CodePrimitiveExpression(0)));

ifLogic.FalseStatements.Add(new CodeMethodReturnStatement(

new CodeBinaryOperatorExpression(

new CodeFieldReferenceExpression(

new CodeThisReferenceExpression(), xProperty.Name),

CodeBinaryOperatorType.Divide,

new CodeFieldReferenceExpression(

new CodeThisReferenceExpression(), yProperty.Name))));

divideMethod.Statements.Add(ifLogic);

As you can see the CodeConditonStatement class has a Condition property that is a CodeBinaryOperatorExpression class. This class is used to create a binary expression. In this example the expression equates to (this.Y == 0). The CodeBinaryOperatorExpression class also has aTrueStatements and a FalseStatements property that enables you to create the code that will be written for the true and false conditions. The preceding code creates the following output:

public double Divide()

{

if ((this.Y == 0))

{

return 0;

}

else

{

return (this.X / this.Y);

}

}

CodeParameterDeclarationExpression and CodeMethodInvokeExpression

The next step is to create the Exponent method. This method takes a parameter called power and returns this.Y raised to that power.

CodeMemberMethod exponentMethod = new CodeMemberMethod();

exponentMethod.Name = "Exponent";

exponentMethod.ReturnType = new CodeTypeReference(typeof(double));

exponentMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;

CodeParameterDeclarationExpression powerParameter =

new CodeParameterDeclarationExpression(typeof(double), "power");

exponentMethod.Parameters.Add(powerParameter);

CodeMethodInvokeExpression callToMath = new CodeMethodInvokeExpression(

new CodeTypeReferenceExpression("System.Math"),

"Pow",

new CodeFieldReferenceExpression(new CodeThisReferenceExpression(),

xProperty.Name), new CodeArgumentReferenceExpression("power"));

exponentMethod.Statements.Add(new CodeMethodReturnStatement(callToMath));

targetClass.Members.Add(exponentMethod);

You use the CodeParameterDeclarationExpression class to create the power parameter. The CodeMethodInvokeExpression class is used to call a method and pass a parameter to the method. The preceding code produces the following output:

public double Exponent(double power)

{

return System.Math.Pow(this.X, power);

}

CodeDOMProvider

The last step is to generate the class file. You use the CodeDOMProvider class to create the file in either C#, VB, or JScript. This class has a method called GenerateCodeFromCompileUnit that takes a CodeCompileUnit, TextWriter, and CodeGeneratorOptions class as parameters. TheCodeGeneratorOptions class has properties that enable you to control the formatting of your automatically generated code. The following sample tells the compiler to use single-line spacing between the member declarations. Setting the BracingStyle property to “C” places the brackets, {}, on separate lines.

CodeDOMProvider provider = CodeDOMProvider.CreateProvider("CSharp");

CodeGeneratorOptions options = new CodeGeneratorOptions();

options.BlankLinesBetweenMembers = false;

options.BracingStyle = "C";

using (StreamWriter sourceWriter = new StreamWriter(@"c:\CodeDOM\Calculator." +

provider.FileExtension))

{

provider.GenerateCodeFromCompileUnit(codeCompileUnit, sourceWriter, options);

}

Lambda Expressions

Lambda expressions are shorthand syntax for creating an anonymous methods. What’s an anonymous method? Well, an anonymous method is essentially a method without a name. What good is a method without a name? Well, when writing methods that are small and used in limited scope, you can write an anonymous method without going through the trouble of creating the method signature. Also, you can pass an anonymous method to other methods to dynamically change how those methods behave. This concept is extremely important to understand before tackling the concept of LINQ. Lambda expressions are used everywhere in LINQ.

Delegates

Before exploring lambda expressions start with the basics. A delegate is a type that references a method. When you declare a delegate, you specify the signature of the method that you want to reference. For example, create a new class called LambdaExpressions and add the following method that takes a string parameter and writes it to the console window:

static void WriteToConsoleForward(string stringToWrite)

{

Console.WriteLine("This is my string: {0}", stringToWrite);

}

If you want to reference this method, first create a delegate that has the same signature.

delegate void MyFirstDelegate(string s);

Notice that the return type is void and the parameter’s type is string which matches the signature of the WriteToConsoleForward method. Now that you have a delegate, you need to associate a variable of this type to the method.

MyFirstDelegate myFirstDelegate = new

MyFirstDelegate(LambdaExpressions.WriteToConsoleForward);

The myFirstDelegate variable essentially holds a reference to the method. You can now call the method by using the myFirstDelegate variable and passing in a parameter.

myFirstDelegate("Hello World");

Now create a second method that takes a string as a parameter and writes the string backward to the console.

static void WriteToConsoleBackwards(string stringToWrite)

{

char[] charArray = stringToWrite.ToCharArray();

Array.Reverse(charArray);

Console.WriteLine("This is my string backwards: {0}",

new string(charArray));

}

Both methods have the same signature, so you can create a single delegate to reference either method. Now create another method that takes the delegate as a parameter and calls the method.

static void WriteToConsole(MyFirstDelegate myDelegate, string stringToWrite)

{

myDelegate(stringToWrite);

}

Now you can call the WriteToConsole method and pass in the method as a parameter.

WriteToConsole(LambdaExpressions.WriteToConsoleForward, "Hello World");

WriteToConsole(LambdaExpressions.WriteToConsoleBackwards, "Hello World");

The preceding two lines of code produce the following output:

This is my string: Hello World

This is my string backwards: dlroW olleH

ADVICE FROM THE EXPERTS: Covariance and Contravariance

Something to note about delegates are the concepts of covariance and contravariance. Covariance enables you to have a method with a more derived return type than the delegate’s return type. So the delegate’s return type could be a base class, and the method’s return type can be a type that inherits from the base class. Contravariance permits parameter types that are less derived than the delegate’s parameter types. So the delegate’s parameters can be a base class, but the method’s parameters can be a class that is derived from the base class.

Anonymous Methods

Anonymous methods are similar to delegates except you don’t have to create the method. You still create the delegate, but you can assign the method all within the same line of code.

MyFirstDelegate forward = delegate(string s2)

{

Console.WriteLine("This is my string: {0}", s2);

};

forward("Hello World");

The preceding code creates a delegate variable called forward, and it references the body of a method. The method can have as many lines as you want. One difference between an anonymous method and a delegate is that you can reference local variables that are not passed as parameters. For example, the following sample creates a delegate that has no parameters. It then creates a local variable and an anonymous method that uses the variable.

delegate void MyAnonymousMethod();

static void Main(string[] args)

{

string myLocalString = "Hello World";

//Create an anonymous method using the local variable.

MyAnonymousMethod forward = delegate()

{

Console.WriteLine(string.Format("This is my string: {0}", myLocalString));

};

forward();

}

This method produces the same output as the previous method. As you can see, programmatically these methods produce the same output, but anonymous methods involve less coding.

Lambda Expressions

Lambda expressions enable you to create an anonymous function using shorthand syntax. Consider the following:

delegate double square(double x);

static void Main(string[] args)

{

square myLambdaExpression = x => x * x;

Console.WriteLine("X squared is {0}", myLambdaExpression(5));

}

The lambda expression is x => x * x. When reading the code, you would say x goes to x times x. The => is called the goes to operator. The left side of the goes to operator evaluates to the input parameters of your method. The body of your method goes on the right side of the goes to operator. In this instance the method can square whatever number is passed into the method. If you need to pass multiple parameters, you use the following syntax:

delegate bool GreaterThan(double x, double y);

static void Main(string[] args)

{

GreaterThan gt = (x, y) => x > y;

Console.WriteLine("Is 6 greater than 5. {0}", gt(6, 5));

}

The preceding code produces the following output:

Is 6 greater than 5. True

When the method contains only a single expression, it is referred to as an expression lambda. When you need multiple statements in the body of the method it is referred to as a statement lambdas. Statement Lambdas are contained in brackets, {}. The following is a lambda expression for theWriteToConsoleBackward method:

s =>

{

char[] charArray = s.ToCharArray();

Array.Reverse(charArray);

Console.WriteLine("This is my string to write backwards: {0}",

new string(charArray));

};

You can also use a lambda expression to pass a function to a method. The following uses a lambda expression to call the WriteToConsole method:

WriteToConsole(x => Console.WriteLine("This is my string {0}", x), "Hello World");

As you can see, the syntax requires less typing as you go from delegate, to anonymous function, to lambda expressions.

Summary

This chapter described a number of topics, starting with reflection. Reflection is a powerful feature in the .NET Framework that enables you to examine all types within an assembly, create instances of classes, invoke methods, read attributes, and perform many other useful operations within your code.

Attributes are metadata that can be applied to a class, a method, or a property. You can use reflection to read attributes and dynamically change the behavior of your application.

You can create custom attribute classes and associate them with your own classes, properties, or methods. Custom attributes are created by creating a class that inherits from the System.Attribute class.

The Code Document Object Model (CodeDOM) is the object model provided by the .NET Framework that enables you to generate code dynamically in either C#, VB, or JScript. Learning the types in the System.CodeDOM namespace is useful when you want to automate tedious tasks or compile code programmatically.

Lambda expressions are shorthand syntax for creating anonymous functions. Anonymous functions are small methods without a signature. Lambda expressions are used extensively in LINQ.

Chapter Test Questions

Read each question carefully and select the answer or answers that represent the best solution to the problem. You can find the answers in Appendix A, “Answers to Chapter Test Questions.”

1. You are given an assignment to create a code generator to automate the task of creating repetitive code. Which namespace contains the types needed to generate code?

a. System.Reflection

b. CodeDom

c. Reflection

d. System.CodeDom

2. Which code can create a lambda expression?

a. delegate x = x => 5 + 5;

b. delegate MySub(double x); MySub ms = delegate(double y) { y * y; }

c. x => x * x;

d. delegate MySub(); MySub ms = x * x;

3. You are consulting for a company called Contoso and are taking over an application that was built by a third-party software company. There is an executable that is currently not working because it is missing a DLL file that is referenced. How can you figure out which DLL files the application references?

a. Create an instance of the Assembly class, load the assembly, and iterate through the ReferencedAssemblies property.

b. Create an instance of the Assembly class, load the assembly, and call the GetReferencedAssemblies method.

c. Create an instance of the Assembly class, load the assembly, and iterate through the Modules property.

d. Create an instance of the Assembly class, load the assembly, and call the GetModulesReferenced method.

4. You are a developer for a finance department and are building a method that uses reflection to get a reference to the type of object that was passed as a parameter. Which syntax can be used to determine an object’s type?

a. Type myType = typeof(myParameter);

b. Object myObject = myParameter.GetType(object);

c. Object myObject = typeof(myParameter);

d. Type myType = myParameter.GetType();

5. You are asked to create a custom attribute that has a single property, called Version, that allows the caller to determine the version of a method. Which code can create the attribute?

a. class MyCustomAttribute :System.Reflection.Attribute { public string Version { get; set; } }

b. class MyCustomAttribute : System.Attribute { public string Version { get; set; } }

c. class MyCustomAttribute : System.AttributeUsage { public string Version { get; set; } }

d. class MyCustomAttribute : System.ClassAttribute { public string Version { get; set; } }

6. Which class in the System.Reflection namespace would you use if you want to determine all the classes contained in a DLL file?

a. FileInfo

b. Assembly

c. Type

d. Module

7. Which method of the Assembly class allows you to get all the public types defined in the Assembly?

a. DefinedTypes

b. Types

c. GetExportedTypes

d. GetModules

8. Which property of the Assembly class returns the name of the assembly?

a. Name

b. FullyQualifiedName

c. Location

d. FullName

9. Which method of the Assembly class returns an instance of the current assembly?

a. GetExecutingAssembly

b. GetAssembly

c. GetCurrentAssembly

d. Assembly

10. Which syntax will Load an Assembly? (Choose all that apply)

a. Assembly.Load("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");

b. Assembly.LoadFrom(@"c:\MyProject\Project1.dll");

c. Assembly.LoadFile(@"c:\MyProject\Project1.dll");

d. Assembly.ReflectionOnlyLoad(("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");

11. Which method should you call if you want the .NET Framework to look in the load-context to load an Assembly?

a. ReflectionOnlyLoad

b. LoadFrom

c. Load

d. LoadFromContext

12. Which method should you call if you want the .NET Framework to look in the load-from context?

a. ReflectionOnlyLoad

b. LoadFrom

c. Load

d. LoadFromContext

13. Which line creates an instance of a DataTable using reflection?

a. myAssembly.CreateInstance("System.Data.DataTable");

b. DataTable.GetType();

c. typeof("System.Data.DataTable");

d. myType.CreateInstance("System.Data.DataTable");

14. Which class would you create if you wanted to determine all the properties contained in a class using reflection?

a. Assembly

b. Class

c. Property

d. Type

15. How can you determine if a class is public or private?

a. Create an instance of the Type class using the typeof keyword and then examine the IsPublic property of the Type variable.

b. Create an instance of the Type class using the typeof keyword and then examine the IsPrivate property of the Type variable.

c. Create an instance of the class within a try catch block and catch the exception if you cannot create an instance of the class.

d. Create an instance of the class and check the IsPublic property.

16. Which class in the System.Reflection namespace is used to represent a field defined in a class?

a. PropertyInfo

b. FieldInfo

c. Type

d. EventInfo

17. Which property of the Type class can you use to determine the number of dimension for an array?

a. GetDimension

b. GetRank

c. GetDimensions

d. GetArrayRank

18. Which statement will returns a private, instance field called "myPrivateField" using reflection? Assume the myClass variable is an instance of a class.

a. myClass.GetType().GetField("myPrivateField", BindingFlags.NonPublic | BindingFlags.Instance)

b. myClass.myPrivateField

c. myClass.GetType().GetField("myPrivateField")

d. myClass. GetType().GetField("myPrivateField", BindingFlags.NonPublic & BindingFlags.Instance)

19. Which method of the MethodInfo class can be used to execute the method?

a. Execute

b. Invoke

c. Call

d. Load

20. Which statement uses reflection to execute the method and passes in two parameters given the following code block?

MyClass myClass = new MyClass();

MethodInfo myMethod = typeof(MyClass).GetMethod("Multiply");

a. myMethod.Execute(myClass, new object[] { 4, 5 });

b. myMethod.Execute(MyClass, new object[] { 4, 5 });

c. myMethod.Invoke(myClass, new object[] { 4, 5 });

d. myMethod.Invoke(MyClass, new object[] { 4, 5 });

Additional Reading and Resources

Here are some additional useful resources to help you in your understanding of the topics presented in this chapter:

Complete list of Microsoft’s System.Reflection namespace classes http://msdn.microsoft.com/en-us/library/system.reflection(v=vs.110).aspx

Microsoft’s Attribute Reference http://msdn.microsoft.com/en-us/library/z0w1kczw(v=vs.110).aspx

Microsoft’s CodeDOM Quick Reference http://msdn.microsoft.com/en-us/library/f1dfsbhc.aspx

Microsoft’s Lambda Expression description http://msdn.microsoft.com/en-us/library/bb397687(v=vs.110).aspx

Code Project.com (a great reference site with a sample of reflection, CodeDOM, and lambda expressions) http://www.codeproject.com/

StackOverflow.com (a great reference site for sample code) http://www.StackOverflow.com

Cheat Sheet

This cheat sheet is designed as a way for you to quickly study the key points of this chapter.

Reflection

· There are two ways to get a reference to the Type object, using typeof() or the .GetType() method on an object.

· You can use the System.Reflection.Assembly class to examine the types within an EXE file or DLL file.

· The Assembly.Load method loads an assembly into memory and enables you to execute code.

· The Assembly.ReflectionOnlyLoad method loads the assembly into memory, but you cannot execute any code.

· The Assembly.CreateInstance method creates an instance of a type.

· The System.Type class represents a class, interface, array, value type, enumeration, parameter, generic type definitions, and open or closed constructed generic types.

· The Type.GetProperty method returns a PropertyInfo object and enables you to set or get a property’s value.

Attributes

· Attributes enable you to create metadata for a class, a property, or a method.

· Attributes are contained in square brackets [] just above the target.

· Custom attributes must inherit from the System.Attribute class.

Code Document Object Model (CodeDOM)

· The CodeDOM is a set of classes that enables you to create code generators.

· The System.CodeDom.CodeCompileUnit class is the top-level class; this is the container for all other object within the class you want to generate.

· The System.CodeDom.CodeDOMProvider class generates the class file in either C#, VB, or JScript.

Lambda expressions

· Lambda expressions are shorthand syntax for anonymous functions.

· A delegate is a type that references a method.

· Covariance enables you to have a method with a more derived return type than the delegate’s return type.

· Contravariance permits parameter types that are less derived than the delegate type.

· The => in a lambda expression is referred to as “goes to.”

Review of Key Terms

anonymous method Enables you to associate a block of code with a delegate without declaring the method signature.

assembly A compiled piece of code in a DLL or EXE file.

attribute Enables you to associate metadata with assemblies, types, methods, properties, and so on.

Code Document Object Model (CodeDOM) Enables the developer to generate code in multiple languages at run time based on a single code set.

context When loading an assembly using reflection, the context is where reflection searches for the assembly.

contravariance Permits parameter types that are less derived than the delegate’s parameter types.

covariance Enables you to have a method with a more derived return type than the delegate’s return type.

delegate A type that references a method.

expression lambda A lambda expression that contains only one statement for the body.

Expression Tree Code in a tree-like structure where each node is an expression.

field A variable defined in a class or struct.

lambda expression Shorthand syntax for an anonymous method that can be associated with a delegate or expressions tree.

load context When loading an assembly using reflection, this context contains the assemblies found by probing.

load-from context When loading an assembly using reflection, this context contains the assemblies located in the pat passed into the LoadFrom method.

module A file that composes an assembly. Typically this is the DLL or EXE file.

probing The process of looking in the GAC, the host assembly store, the folder of the executing assembly, or the private bin folder of the executing assembly to find an assembly.

reflection Provides classes that can be used to read metadata or dynamically invoke behavior from a type.

reflection-only context When loading an assembly using reflection, this is the context that contains the assemblies loaded with the ReflectionOnlyLoad and ReflectionOnlyLoadFrom methods.

statement lambda A lambda expression with more than one statement in the body of the expression.

target The class, property, or method that contain metadata defined by an attribute.

type Any class, interface, array, value type, enumeration, parameter, generic type definition, and open or closed constructed generic type.

EXAM TIPS AND TRICKS

The Review of Key Terms and the Cheat Sheet for this chapter can be printed off to help you study. You can find these files in the ZIP file for this chapter at www.wrox.com/remtitle.cgi?isbn=1118612094 on the Download Code tab.