You can split the properties Implemented by Type into three categories:
- A number of properties retrieve the strings containing various names associated with the lass, as shown in the following table:
- It is also possible ‘to retrieve references to further type objects that represent related classes, as shown in the following table:
- A number of Boolean properties indicate whether this type is, for example, a class, an enum, and so on. These properties include IsAbstract, IsArray, IsClass, IsEnurn, Is Interface,IsPointer, IsPrimitive (one of the predefined primitive data types), ISPublic, IsSealed, and IsValueType.
For example, using a primitive data type:
Or using the Vector class:
You can also retrieve a reference to the assembly that the type is defined in. This is returned as a reference to an instance of the System. Reflection. Assembly class, which is examined shortly:
Type t = typeof (Vector);
Assembly containingAssembly = new Assembly(t);
Most of the methods of System. Type are used to obtain details of the members of the corresponding data type – the constructors, properties, methods, events, and so on. Quite a large number of methods exist, but they all follow the same pattern. For example, two methods retrieve details of the methods of the data type: GetMethod () and GetMethods (). GetMethod () returns a reference to a System. Reflection.MethodInfo object, which contains details of a method. GetMethods () returns an array of such references. The difference is that GetMethods () returns details of all the methods, whereas GetMethoQ () returns details of just one method with a specified parameter list. Both methods have overloads that take an extra parameter, a BindingFlags enumerated value that indicates which members should be returned – for example, whether to return public members, instance members, static members, and so on.
For example, the simplest overload of GetMethods () takes no parameters and returns-details of all the public methods of the data type:
Type t = typeof(double);
MethodInfo methods = t.GetMethods();
foreach (Methodlnfo nextMethod in methods)
The member methods of Type that follow the same pattern are shown in the following table.
The GetMember () and GetMembers () methods return details of any or all members of the data type, regardless of whether these members are constructors, properties, methods, and so on. Finally, note that it is possible to invoke members either by calling the InvokeMember () method of Type or by calling the Invoke () method of the MethodInfo, PropertyInfo, and the other classes.
The TypeView Example
This section demonstrates some of the features of the Type class with a short example, TypeView, which you can use to list the members of a data type. The example demonstrates how to use TypeView for a double; however, you can swap this type with any other data type just by changing one line of the code in the sample. TypeView displays far more information than can be displayed in a console window, so we’re going to take a break from our normal practice and display the output in a message box. Running TypeView for a double produces the results shown:
The message box displays the name, full name, and namespace of the data type as well as the name of the underlying type and the base type. Next, it simply iterates through all the public instance members of the data type, displaying for each member the declaring type, the type of member (method, field, and so on), and the name of the member. The declaring type is the name of the class that actually declares the type member (for example, System. Double if it is defined or overridden in System. Double, or the name of e relevant base type if the member is simply inherited from some base class).
TypeView does not display signatures of methods because you are retrieving details of all public instance members through MernberInfo objects, and information about parameters is not available through aMemberInfo object. In order to retrieve that information, you would need references to MethodInfo and other more specific objects, which means that you would need to obtain details of each type of member separately.
TypeView does display details of all public instance members, but for doubles, the only ones defined are fields and methods. For this example, you will compile TypeView as a console application – there is no problem with displaying a message box from a console application. However, the fact that you are using a message box means that you need to reference the base cJassassembly System. windows. Forms. dIl, which contains the classes in the System. Windows.Forms namespace in which the MessageBox class that you will need is defined. The code for TypeViewis as follows. To begin, you need to add a few using statements:
using System. Reflection;
You need System. Text because you will be using a StringBuilder object to build up the that to be to displayed in the message box, and System. windows. Forms for the message box itself. entire code is in one class, MainClass, which has a couple of static methods and one static field, a StringBuilder instance called OutputText, which will be used to build up the text to be displayed in
the message box. The main method and class declaration look like this:
The Main () method implementation starts by declaring a Type object to represent your chosen data type. You then call a method, AnalyzeType ( )r which extracts the information from the Type object and uses it to build up the output text. Finally, you show the output in a message box. Using the MessageBox class is fairly intuitive. You just call its static Show () method, passing it two strings, which will, respectively, be the text in the box and the caption. AnalyzeType () is where the bulk of the work is done:
You implement the AnalyzeType () method by calling various properties of the Type object to get the information you need concerning the type names, then call the GetMembers () method to get an array of Memberlnfo objects that you can use to display the details of each member. Note that you use a helper method, AddToOutput (),to build up the text to be displayed in the message box:
The Assembly Class
The Assembly class is defined in the System. Reflection namespace and provides access to the metadata for a given assembly. It also contains methods to allow you to load and Even execute an assembly – assuming that the assembly is an executable. Like the Type class, Assembly contains a large number o~methods and properties – too many to cover here. Instead, this section is confined to covering those methods and properties that you need to get started and that you will use to complete the WhatsNewAttributes example.
Before you can do anything with an Assembly instance, you need to load the corresponding assembly into the running process. You can do this with either the static members Assembly. Load () or Assembly. LoadFrom ()..The difference between these methods is that Load () takes the name of the assembly, and the runtime searches in a variety of locations in an attempt to locate the assembly. These locations include the local directory and the global assembly cache. LoadFrom () takes the full path name of an assembly and does no! attempt to find the assembly in any other location:
A number of other overloads of both methods exist, which supply additional security information. Once you have loaded an assembly, you can use various properties on it to find out, for example, its full name:
string name = assemblyl.FullName;
Finding Out About Types Defined In an Assembly
One nice feature of the Assembly class is that it allows you to obtain details of all the types that are defined in the corresponding assembly. You simply call the Assembly. Get Types () method, which returns an array of System. Type references containing details of all the types. You can then manipulate these Type references as explained in the previous section.
Finding Out About Custom Attributes
The methods you use to find out which custom attributes are defined on an assembly or type depend on what type of object the attribute is attached to. If you want to find out what custom attributes are attached to an assembly as a whole, you need to call a static method of the Attribute class, GetCustomAttributes (), passing in a reference to the assembly:
This is actually quite significant. You may have wondered why, when you defined custom attributes, you had to go to all the trouble of actually writing classes for them, and why Microsoft hadn’t come up with some simpler syntax. Well, the answer is here. The custom attributes do genuinely exist as objects, and when an assembly is loaded you can read in these attribute objects, examine their properties, and call their methods.
GetCustomAttributes (), which is used to get assembly attributes, has a few overloads. If you call it without specifying any parameters other than an assembly reference, it will simply return all the custom
attributes defined for that assembly. You can also call GetCustOmAttributes () specifying a second parameter, which is a Type object that indicates the attribute class in which you are interested. In this .case, GetCustomAttributes () returns an array consisting of all the attributes present that are of the specified type.
Note that all attributes are retrieved as plain Attribute references. If you want to call any of the methods or properties you defined for your custom attributes, you will need to cast these references explicitly to the relevant custom attribute classes. You can obtain details of custom attributes that are attached to a given data type by calling another overload of Assembly. GetCustomAttributes ( ), this time passing a Type reference that describes the type for which you want to retrieve any attached attributes. If you want to obtain attributes that are attached to methods, constructors, fields, and so on, however, you will need to call a GetCustomAttributes () method that is a member of one of the classes Methodlnfo, Constructorlnfo, Fieldlnfo, and so on..
If you expect only a single attribute of a given type, you can call the GetCustomAttribute () method instead, which returns a single Attribute object. You will use GetCustomAttribute () in the WhatsNewAttributes example to find out whether the SupportsWhatsNew attribute is present in the assembly. To do this, you call GetCustomAttribute (), passing in a reference to the WhatsNewAttributes assembly, and the type of the SupportsWhatsNewAttribute attribute. If this attribute is present, you get an Attribute instance. If no instances of it are defined in the assembly, you get null. And if two or more instances are found, GetCustomAttribute () throws a System. Reflection.AmbiguousMatchException.
Attribute supportsAttribute =. Attribute. GetCustomAttributes (assemblyl, typeof(SupportsWhatsNewAttribute));
Completing the WhatsNewAttributes Example
You now have enough information to complete the WhatsNewAttributes example by writing the source code for the final assembly in the sample, the LookUpWhatsNew probably. this part of the application is a console application. However, it needs to reference the other assemblies of WhatsNewAttributes and VectorClass. Although this is going to be a command-line application, you will follow the previous TypeView sample in actually displaying your results in a message box because there is a lot of text output – too much to show in a console window screenshot.
The file is called LookUpWhatsNew. cs, and the command to compile it is:
csc /referencezWhatsNewAttributes.dll Ireference:Vectorclass.dll LookUpWhatsNew.cs
In the source code of this file,you first indicate the namespaces you want to infer.System.Text is there because you need to use a StringBuilder object again:
The class that contains the main program entry point as well as the other methods is WhatsNewChecker. All the methods you define are in this class,which also has two static fields: output Text, which
contains the text as you build it up in preparation for writing it to the message box, and backDateTo, which stores the date you have selected.All modifications made since thisdate will be displayed. Normally, you’would display a dialog box inviting the user to pick this date,but we don’t want to get side tracked into that kind of code. For this reason,back DateTo is hard-coded to a value of 1 Feb 2008. You can easily change this date if you want when you download the code:
The Main () method first loads the VectorClass assembly, and verifies that it is marked with the SupportsWhatsNew attribute.You know VectorClass has the SupportsWhatsNew attribute applied to it because you have only recently compiled it,but this is a check that would be worth making if users were given a choice of what assembly they wanted to check.
Assuming that all is well, you use the Assembly. Get Types () method to get an array of all the types defined in this assembly, and then loop through them. For each one, you calla method, DisplayTypeInfo (),which will add the relevant text,including details of any instances of LastModifiedAttribute, to the outputText field.Finally,you show the message box with the complete text.The DisplayTypeInfo () method looks like this
Notice that the first thing you do in this method is check whether the Type reference you have been passed actually represents a class. Because, in order to keep things simple, ,you have specified that the LastModified attribute can be applied only to classes or member methods, you would be wasting your time doing.any processing if the item is not a class (it could be a class, delegate, or enum).
Next, you use the Attribute .GetCustomAttributes () methodto find ent if this class does have any LastModifiedAt tribute instances attached to it. If it does, you add their details to the output text, using a helper method, WriteAttributeInfo ().
Finally, you use the Type. GetMethods () method to iterate through all the member methods of this data type, and then do the same with each method as you did for the class – check if it has any LastModifiedAttribute instances attached to it and, if so, display them using WriteAttributeInfo().
The next bit of code shows the WriteAttributeInfo () method, which is responsible for working out what text to display for a given LastModifiedAttribute instance. Note that this method is passed an Attribute reference, so it needs to cast this to a LastModifiedAttribute reference first. After it has done that, it uses the properties that you originally defined for this attribute to retrieve its,parameters. It checks that the date of the attribute is sufficiently recent before actually adding it to the text for display:
Running this code produces the.results shown:
Notice that when you list the types defined in the VectorClass assembly, you actually pick up two classes: Vector and the embedded VectorEnumerator class. Also notice that because the backDateTo date of 1 Feb is hard-coded in this example, you actually pick up the attributes that are dated 14Feb (when you added the collection support) but not those dated 14Jan (when you added the IFormattable interface).