Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Reflection Performance

Reflection is an extremely powerful mechanism because it allows you to discover and use types and members at run time that you did not know about at compile time. This power does come with two main drawbacks:

■ Reflection prevents type safety at compile time. Because reflection uses strings heavily, you lose type safety at compile time. For example, if you call Type.GetType("int"); to ask reflection to find a type called “int”, the code compiles but returns null at run time because the CLR knows the “int” type as “System.Int32”.

■ Reflection is slow. When using reflection, the names of types and their members are not known at compile time; you discover them at run time by using a string name to identify each type and member. This means that reflection is constantly performing string searches as the types in the System.Reflection namespace scan through an assembly’s metadata. Often, the string searches are case-insensitive comparisons, which can slow this down even more.


Invoking a member by using reflection will also hurt performance. When using reflection to invoke a method, you must first package the arguments into an array; internally, reflection must unpack these on to the thread’s stack. Also, the CLR must check that the arguments are of the correct data type before invoking a method. Finally, the CLR ensures that the caller has the proper security permis- sion to access the member being invoked.

For all of these reasons, it’s best to avoid using reflection to access a field or invoke a method/ property. If you’re writing an application that will dynamically discover and construct type instances, you should take one of the following approaches:

■ Have the types derive from a base type that is known at compile time. At run time, construct an instance of the derived type, place the reference in a variable that is of the base type (by way of a cast), and call virtual methods defined by the base type.

■ Have the type implement an interface that is known at compile time. At run time, construct an instance of the type, place the reference in a variable that is of the interface type (by way of a cast), and call the methods defined by the interface.

I tend to prefer using the interface technique over the base type technique because the base type technique doesn’t allow the developer to choose the base type that works best in a particular situation. Although the base type technique works better in versioning scenarios, because you could always add a member to the base type and the derived types just inherit it; you can’t add a mem- ber to an interface without forcing all types that implement the interface to modify their code and recompile it.

When you use either of these two techniques, I strongly suggest that the interface or base type be defined in its own assembly. This will reduce versioning issues. For more information about how to do this, see the section titled “Designing an Application That Supports Add-Ins” later in this chapter.



 

Discovering Types Defined in an Assembly

Reflection is frequently used to determine what types an assembly defines. The FCL offers many APIs to get this information. By far, the most commonly used API is Assembly’s ExportedTypes. prop- erty. Here is an example of code that loads an assembly and shows the names of all of the publicly exported types defined in it.

 

using System;

using System.Reflection;

 

public static class Program { public static void Main() {

String dataAssembly = "System.Data, version=4.0.0.0, " + "culture=neutral, PublicKeyToken=b77a5c561934e089";

LoadAssemAndShowPublicTypes(dataAssembly);

}

 

private static void LoadAssemAndShowPublicTypes(String assemId) {

// Explicitly load an assembly in to this AppDomain Assembly a = Assembly.Load(assemId);


// Execute this loop once for each Type

// publicly­exported from the loaded assembly foreach (Type t in a.ExportedTypes) {

// Display the full name of the type Console.WriteLine(t.FullName);

}

}

}

 

 

What Exactly Is a Type Object?

Notice that the previous code iterates over a sequence of System.Type objects. The System.Type type is your starting point for doing type and object manipulations. A System.Type object repre- sents a type reference (as opposed to a type definition).

Recall that System.Object defines a public, nonvirtual instance method named GetType. When you call this method, the CLR determines the specified object’s type and returns a reference to its Type object. Because there is only one Type object per type in an AppDomain, you can use equality and inequality operators to see whether two objects are of the same type.

 

private static Boolean AreObjectsTheSameType(Object o1, Object o2) { return o1.GetType() == o2.GetType();

}

 

In addition to calling Object’s GetType method, the FCL offers several more ways to obtain a

Type object:

 

■ The System.Type type offers several overloaded versions of the static GetType method. All versions of this method take a String. The string must specify the full name of the type (in- cluding its namespace). Note that the primitive type names supported by the compiler (such as C#’s int, string, bool, and so on) aren’t allowed because these names mean nothing to the CLR. If the string is simply the name of a type, the method checks the calling assembly to see whether it defines a type of the specified name. If it does, a reference to the appropriate Type object is returned.

If the calling assembly doesn’t define the specified type, the types defined by MSCorLib.dll are

checked. If a type with a matching name still can’t be found, null is returned or a System. TypeLoadException is thrown, depending on which overload of the GetType method you called and what parameters you passed to it. The FCL documentation fully explains this method.

You can pass an assembly-qualified type string, such as “System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”, to GetType. In this case, GetType will look for the type in the specified assembly (loading the assembly if necessary).

■ The System.Type type offers a static ReflectionOnlyGetType method. This method be- haves similarly to the GetType method mentioned in the previous bullet, except that the type is loaded so that it can be reflected over but cannot be executed.


■ The System.TypeInfo type offers the following instance members: DeclaredNestedTypes

and GetDeclaredNestedType.

 

■ The System.Reflection.Assembly type offers the following instance members: GetType, DefinedTypes, and ExportedTypes.

       
   
 
 

 

Many programming languages also offer an operator that allows you to obtain a Type object from a type name that is known at compile time. When possible, you should use this operator to obtain

a reference to a Type instead of using any of the methods in the preceding list, because the opera- tor generally produces faster code. In C#, the operator is called typeof, and you use this operator typically to compare late-bound type information with early-bound (known at compile time) type information. The following code demonstrates an example of its use.

 

private static void SomeMethod(Object o) {

// GetType returns the type of the object at runtime (late­bound)

// typeof returns the type of the specified class (early­bound) if (o.GetType() == typeof(FileInfo)) { ... }

if (o.GetType() == typeof(DirectoryInfo)) { ... }

}

       
   
 
 

 

 

As mentioned earlier, a Type object represents a type reference that is a lightweight object. If you want to learn more about the type itself, then you must acquire a TypeInfo object, which represents a type definition. You can convert a Type object to a TypeInfo object by calling System.Reflection.IntrospectionExtensions’ GetTypeInfo extension method.

 

Type typeReference = ...; // For example: o.GetType() or typeof(Object) TypeInfo typeDefinition = typeReference.GetTypeInfo();


And, although less useful, you can convert a TypeInfo object to a Type object by calling Type­ Info’s AsType method.

 

TypeInfo typeDefinition = ...;

Type typeReference = typeDefinition.AsType();

 

Obtaining a TypeInfo object forces the CLR to resolve the type by ensuring that the assembly that defines the type is loaded. This can be an expensive operation that can be avoided if all you need are type references (Type objects). However, after you have a TypeInfo object, you can query many of the type’s properties to learn more about it. Most of the properties, such as IsPublic, IsSealed, IsAbstract, IsClass, IsValueType, and so on, indicate flags associated with the type. Other properties, such as Assembly, AssemblyQualifiedName, FullName, Module, and so on, return the name of the type’s defining assembly or module and the full name of the type. You can also query the BaseType property to obtain a reference to the type’s base type, and a slew of members will give you even more information about the type. The FCL documentation describes all of the methods and properties that TypeInfo exposes.

 

Building a Hierarchy of Exception-Derived Types

The following code uses many of the concepts discussed already in this chapter to load a bunch of as- semblies into the AppDomain and display all of the classes that are ultimately derived from System. Exception. By the way, this is the program I wrote to build the exception hierarchy displayed in the “FCL-Defined Exception Classes” section in Chapter 20, “Exceptions and State Management.”

 

public static void Go() {

// Explicitly load the assemblies that we want to reflect over LoadAssemblies();

 

// Filter & sort all the types var allTypes =

(from a in AppDomain.CurrentDomain.GetAssemblies() from t in a.ExportedTypes

where typeof(Exception).GetTypeInfo().IsAssignableFrom(t.GetTypeInfo()) orderby t.Name

select t).ToArray();

 

// Build the inheritance hierarchy tree and show it Console.WriteLine(WalkInheritanceHierarchy(new StringBuilder(), 0, typeof(Exception),

allTypes));

}

 

private static StringBuilder WalkInheritanceHierarchy(

StringBuilder sb, Int32 indent, Type baseType, IEnumerable<Type> allTypes) { String spaces = new String(' ', indent * 3);

sb.AppendLine(spaces + baseType.FullName); foreach (var t in allTypes) {

if (t.GetTypeInfo().BaseType != baseType) continue; WalkInheritanceHierarchy(sb, indent + 1, t, allTypes);


}

return sb;

}

 

 

private static void LoadAssemblies() { String[] assemblies = {

"System, PublicKeyToken={0}",

"System.Core, PublicKeyToken={0}",

"System.Data, PublicKeyToken={0}",

"System.Design, PublicKeyToken={1}", "System.DirectoryServices, PublicKeyToken={1}", "System.Drawing, PublicKeyToken={1}", "System.Drawing.Design, PublicKeyToken={1}", "System.Management, PublicKeyToken={1}", "System.Messaging, PublicKeyToken={1}", "System.Runtime.Remoting, PublicKeyToken={0}", "System.Security, PublicKeyToken={1}", "System.ServiceProcess, PublicKeyToken={1}", "System.Web, PublicKeyToken={1}", "System.Web.RegularExpressions, PublicKeyToken={1}", "System.Web.Services, PublicKeyToken={1}", "System.Xml, PublicKeyToken={0}",

};

 

String EcmaPublicKeyToken = "b77a5c561934e089"; String MSPublicKeyToken = "b03f5f7f11d50a3a";

 

// Get the version of the assembly containing System.Object

// We'll assume the same version for all the other assemblies Version version = typeof(System.Object).Assembly.GetName().Version;

 

// Explicitly load the assemblies that we want to reflect over foreach (String a in assemblies) {

String AssemblyIdentity =

String.Format(a, EcmaPublicKeyToken, MSPublicKeyToken) + ", Culture=neutral, Version=" + version;

Assembly.Load(AssemblyIdentity);

}

}

 

 

Constructing an Instance of a Type

After you have a reference to a Type-derived object, you might want to construct an instance of this type. The FCL offers several mechanisms to accomplish this:

System.Activator’s CreateInstance methodsThe Activator class offers several overloads of its static CreateInstance method. When you call this method, you can pass either a reference to a Type object or a String that identifies the type of object you want to create. The versions that take a type are simpler. You get to pass a set of arguments for the type’s constructor, and the method returns a reference to the new object.

The versions of this method in which you specify the desired type by using a string are a bit

more complex. First, you must also specify a string identifying the assembly that defines the


type. Second, these methods allow you to construct a remote object if you have remoting options configured properly. Third, these versions don’t return a reference to the new object. Instead, they return a System.Runtime.Remoting.ObjectHandle (which is derived from System.MarshalByRefObject).

An ObjectHandle is a type that allows an object created in one AppDomain to be passed around to other AppDomains without forcing the object to materialize. When you’re ready to materialize the object, you call ObjectHandle’s Unwrap method. This method loads the assembly that defines the type being materialized in the AppDomain where Unwrap is called. If the object is being marshaled by reference, the proxy type and object are created. If the object is being marshaled by value, the copy is deserialized.

System.Activator’s CreateInstanceFrom methodsThe Activator class also offers a set of static CreateInstanceFrom methods. These methods behave just as the Create­ Instance method, except that you must always specify the type and its assembly via string parameters. The assembly is loaded into the calling AppDomain by using Assembly’s Load­ From method (instead of Load). Because none of these methods takes a Type parameter, all of the CreateInstanceFrom methods return a reference to an ObjectHandle, which must be unwrapped.

System.AppDomain’s methodsThe AppDomain type offers four instance methods (each with several overloads) that construct an instance of a type: CreateInstance, Create­ InstanceAndUnwrap, CreateInstanceFrom, and CreateInstanceFromAndUnwrap. These methods work just as Activator’s methods except that these methods are instance methods, allowing you to specify which AppDomain the object should be constructed in. The methods that end with Unwrap exist for convenience so that you don’t have to make an additional method call.

System.Reflection.ConstructorInfo’sInvoke instance methodUsing a reference to a TypeInfo object, you can bind to a particular constructor and obtain a reference to the

constructor’s ConstructorInfo object. Then you can use the reference to the Constructor­ Info object to call its Invoke method. The type is always created in the calling AppDomain, and a reference to the new object is returned. I’ll also discuss this method in more detail later in this chapter.

       
   
 
 


The mechanisms just listed allow you to create an object for all types except for arrays (System. Array-derived types) and delegates (System.MulticastDelegate-derived types). To create an ar- ray, you should call Array’s static CreateInstance method (several overloaded versions exist). The first parameter to all versions of CreateInstance is a reference to the Type of elements you want in the array. CreateInstance’s other parameters allow you to specify various combinations of dimen- sions and bounds. To create a delegate, you should call MethodInfo’s CreateDelegate method. The first parameter to all versions of CreateDelegate is a reference to the Type of delegate you want to create. CreateDelegate’s other parameter allows you to specify which object should be passed as the this parameter when calling an instance method.

To construct an instance of a generic type, first get a reference to the open type, and then call Type’s MakeGenericType method, passing in an array of types that you want to use as the type ar- guments. Then, take the returned Type object and pass it into one of the various methods previously listed. Here is an example.

 

using System;

using System.Reflection;

internal sealed class Dictionary<TKey, TValue> { } public static class Program {

public static void Main() {

// Get a reference to the generic type's type object Type openType = typeof(Dictionary<,>);

 

// Close the generic type by using TKey=String, TValue=Int32

Type closedType = openType.MakeGenericType(typeof(String), typeof(Int32));

 

// Construct an instance of the closed type Object o = Activator.CreateInstance(closedType);

 

// Prove it worked Console.WriteLine(o.GetType());

}

}

 

If you compile the preceding code and run it, you get the following output.

 

Dictionary`2[System.String,System.Int32]

 


Date: 2016-03-03; view: 785


<== previous page | next page ==>
Nbsp;   Using Reflection to Build a Dynamically Extensible Application | Nbsp;   Designing an Application That Supports Add-Ins
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.01 sec.)