Chapter 11. Reflection Fundamentals

IN THIS CHAPTER

Reflection is a runtime facility that allows you to write code that can interrogate data types at runtime. This means that you can obtain information about the data type of variables. More than that, it means that you can obtain information about class members, properties, methods, fields, constructors, and much more.

This chapter introduces you to the basics of reflection: what it is and how it works. Then, you will see how to use reflection to work with methods, members (fields, properties, and so on), events and more. Finally, you will see how reflection works to allow you to create and consume custom attributes that can be used to decorate your class with metadata that can be read at runtime.

Introduction to Reflection

Reflection is what allows code to interrogate type information and metadata at runtime. One core fact allows this to happen: In the .NET Framework, data types are objects. This means that every data type—whether it is a class you wrote or a part of the base class library—has methods you can invoke and properties you can examine. This allows you to do things like obtain the list of methods exposed by a class, or determine the data type of a property, or even iterate through the list of parameters to a given method. You can even do things like obtain references to resources embedded with an assembly, though assemblies will be covered in Chapter 12.

In unmanaged languages, such as unmanaged C++, memory is just a collection of bytes. If you declare an array that will consume 32 bytes of memory, C++ has absolutely no idea what is contained in that 32 bytes. In contrast, the Common Language Runtime (CLR) knows exactly what is contained in every piece of memory consumed. Not only does it know where the data starts and stops, but it knows what kind of data it is. Without having access to the source code, you can use reflection to find out the data type of something in memory, and then use additional reflection tools to interrogate that type to find out its parent classes, the interfaces it implements, the methods it exposes, the properties it contains, and even the events it hosts.

A great deal of work with reflection begins by getting a reference to the data type of a particular variable in memory. This is done by calling the GetType() method on an object. This returns an object of type System.Type. As mentioned earlier, all data types within the .NET Framework are objects. Table 11.1 lists some of the most commonly used methods and properties of the Type class, the core of the Reflection functionality in the .NET Framework.

Table 11.1 System.Type Members

Image

Using this table of properties and methods, you can now start to work with specific aspects of reflection, as discussed throughout the remainder of this chapter.

Working with Method Information

Method information centers on the MethodInfo class. You can obtain an instance of this through the GetMethod or GetMethods methods on the Type object, as shown in the following example:

Type t = cust.GetType();
MethodInfo method = t.GetMethod("DoSomething");

Before diving into a code example, let’s take a look at Table 11.2, which lists some of the common properties and methods available for the MethodInfo class.

Table 11.2 MethodInfo Properties and Methods

Image

Now let’s see some of these methods and properties in action, as shown in the sample code in Listing 11.1.

Listing 11.1 Reflecting on Methods and Parameters

Image

Image

The output of the preceding code looks as follows:

Image

The Customer class on which the preceding code is reflecting is shown here:

Image

One thing that might be somewhat surprising for developers who have prior experience with reflection is that generics are completely compatible with reflection. Using reflection, you can obtain all information about a method, even information related to generic type parameters. Also, if you look back at the otherCustomer and newCustomer parameters in the console output, you can see that those parameters aren’t indicated as being generic. In fact, you can see that as far as the runtime is concerned, those parameters are of type Customer<string>.

Working with Member Information

Working with members is slightly different than working with methods. The reason for this is that there are many different member types, including properties, fields, methods, constructors, and so on. When you obtain a MemberInfo instance, you can take a look at the MemberType property to determine which kind of member you’re looking at.

Table 11.3 gives you a quick overview of some of the more common properties and methods of the MemberInfo class.

Table 11.3 MemberInfo Properties and Methods

Image

When dealing with members of a specific type, you can obtain more detailed information about that member using the appropriate class. For example, you can get more detailed information about a property from the PropertyInfo class. Similar classes are available, such as ConstructorInfo, MethodInfo, FieldInfo, EventInfo, and ParameterInfo. Each of these classes has a corresponding Getxxxx method on the containing type. For instance, to obtain a specific field, you would use the GetField method on the type itself, which returns a FieldInfo instance.

In some cases, when you attempt to retrieve a member, you can pass a BindingFlags value. This value is a bitwise-OR’d list of scope items that tell reflection what types of items you want to search for when attempting to locate a member.

Table 11.4 shows a brief summary of some of the different values that you can use for the BindingFlags enumeration when locating type members. This isn’t the entire list. For the entire list, you can look up the BindingFlags enumeration in the online MSDN documentation.

Table 11.4 BindingFlags Enumeration Members

Image

Listing 11.2 shows an example of accessing member information through Reflection. Note that for simplicity, the Customer class from the preceding sample was reused.

Listing 11.2 Reflecting on Member Information

Image

Image

The keen (or suspicious) eye may have already noticed that the preceding code can actually read data from a private member, even on a class that is completely unrelated to the one making the Reflection calls. This is always a concern, and as such you should never assume that even your in-memory data is secure. You will see some ways in which you can protect your data in-memory in Chapter 15, “Cryptography and Data Protection.”

The output from the preceding sample produces text that looks like the following:

Image

Examining Events

As you saw in Chapter 9, “Events and Delegates,” events are an extremely powerful feature that work with delegates that match a specific method signature to signal important events. You can then “add” your delegate to an event hosted by another class. When that event is triggered, your delegate will be among those methods invoked as a result. In this way, you can write code that will subscribe to be notified when important changes take place or important events need to be signaled.

Using reflection, you can dynamically obtain information about an event at runtime.

The code in Listing 11.3 shows how to use the EventInfo class to obtain detailed information about an event, as well as add and remove event handlers. A new delegate and an event called OnPropertyChanged were added to the original Customer class used in the first sample for the purpose of this demonstration.

Listing 11.3 Reflecting on Events

Image

Image

Creating and Examining Custom Code Attributes

Creating custom attributes is actually a pretty simple process. A custom code attribute is really just a class that inherits from System.Attribute. After you create a class based on the Attribute class, you can then decorate additional classes with that attribute to serve as metadata that can be read at runtime.

There are many reasons why you would want to associate custom attributes with your code. Some of the attributes that are already part of the framework allow you to control the transactional behavior of classes, the serialization behavior, the COM visibility of other classes, and much more.

Listing 11.4 shows an example of a class that inherits from Attribute. You use private members, constructors, and properties for providing access to the metadata information that will be supplied to the class at runtime through attribute parameters.

Listing 11.4 Custom Code Attributes: The DataColumnAttribute Class

Image

Image

It looks like a fairly simple class. In fact, it is quite simple. All it’s maintaining is a couple of bits of information about how to relate a property on a class to a column in a database, a common data access task. Listing 11.5 shows a class called Shape that has attributes that show what it looks like when you associate custom code attributes with a class.

Listing 11.5 Custom Code Attributes: The Shape Class (Attribute Decoration Sample)

Image

Finally, Listing 11.6 shows how you can use reflection to obtain references to the custom code attributes on a class instance at runtime.

Listing 11.6 Using Reflection to Query Custom Attributes at Runtime

Image

Summary

This chapter has shown you the power of reflection. As you read through this chapter, you learned that in the .NET Framework, data types are also objects that can be treated just like any other object. Data types have methods and properties that can be accessed the same way you access normal object methods and properties. These members allow you to utilize reflection to find out information about methods, members, events, data types, and much more.

Using reflection isn’t just a way to find out information about your own code. Having the ability to inspect data types at runtime is an extremely valuable tool. When you find a need for reflection in your own application, you will wonder how you ever managed to write code without it.

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

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