GOTCHA 10 Type.GetType() may not locate all types


GOTCHA #10 Type.GetType() may not locate all types

The Abstract Factory pattern [Freeman04, Gamma95] is a common and useful pattern that abstracts object creation. It isolates the code that decides which type of object to create from the code that uses the objects. It is almost effortless in .NET to use Abstract Factory due to the powerful GetType() method of the Type class.

Suppose you need to create different kinds of objects depending on runtime conditions. Perhaps the name of the class is read from a configuration file, or provided as an input to the program. Or you may be dealing with plug-ins that are dynamically introduced when an application is launched (or even while it is running). How do you create an object when you don't know what class it belongs to until the moment you need to create it?

The Type.GetType() method can help you achieve this. Here's how to use it:

  1. Obtain the class's type information by calling Type.GetType().

  2. Use the Activator.CreateInstance() method to create an object of that class, assuming you have a no-parameter constructor.

  3. Cast the object reference (using the as operator in C# or CType function in VB.NET) to a known interface (that the class implements) and invoke the interface methods on it.

This flexibility paves the way for a lot of extensibility in applications.

The core of this facility is the Type class's GetType() method. In writing an application, if you pass the GetType() method the name of a class in your assembly, it will fetch the Type metadata for that class. However, when you ask for type information for a class or plug-in from another assembly, GetType() will fail.

Consider Example 1-24. It is a simple WinForm application with a button. When the button is clicked, you get the Type object for three types and display the information on them.

Example 1-24. Behavior of Type.GetType()

C# (GetType)

         private void CallGetTypeButton_Click(object sender,             System.EventArgs e)         {             try             {                 Type theType;                               theType = Type.GetType(                     "CallingGetType.Form1", true);                 MessageBox.Show("First type is "                     + theType.FullName);                 theType = Type.GetType(                     "System.Collections.Queue", true);                 MessageBox.Show("Second type is "                     + theType.FullName);                 theType = Type.GetType(                     "System.Windows.Forms.Form",                     true);                 MessageBox.Show("Third type is "                     + theType.FullName);             }             catch(Exception ex)             {                 MessageBox.Show("Error: " + ex.Message);             }         } 

VB.NET (GetType)

     Private Sub CallGetTypeButton_Click( _         ByVal sender As System.Object, _         ByVal e As System.EventArgs) _         Handles CallGetTypeButton.Click         Try             Dim theType As Type             theType = Type.GetType( _              "CallingGetType.Form1", True)             MessageBox.Show("First type is " _              & theType.FullName)             theType = Type.GetType( _             "System.Collections.Queue", True)             MessageBox.Show("Second type is " _              & theType.FullName)             theType = Type.GetType( _              "System.Windows.Forms.Form", _              True)             MessageBox.Show("Third type is " _              & theType.FullName)         Catch ex As Exception             MessageBox.Show("Error: " & ex.Message)         End Try     End Sub 

Figure 1-16, Figure 1-17, and Figure 1-18 show the output from the code in Example 1-24.

Figure 1-16. CallingGetType.Form1


Figure 1-17. System.Collections.Queue


Figure 1-18. System.Windows.Forms.Form


While there was no problem getting the Type metadata for Form1 and System.Collections.Queue, the call to GetType() with the class name System.Windows.Forms.Form failed. (The Form class is the base class of Form1 within which this code is executing).

In developing code that creates objects based on runtime class information, and applications that require the use of plug-ins, you may run into problems like this. If you test your application using plug-in components that are part of your own assembly, they'll probably work just fine. However, when you try to load a class from another assembly, things won't work quite so well. The reason is that GetType(), if given a class name, only looks in the calling object's assembly and mscorlib.dll. Since Form1 belongs to the calling assembly and System.Collections.Queue belongs to mscorlib.dll, there is no problem with them.

There is a workaround that enables you to load classes from other assemblies, like System.Windows.Forms. When you tell GetType() which class you need, you must specify the full name of the class including the full identity of the assembly. This identity includes the name, version, culture information, and the strong name public key token.

An example of correct usage of GetType() for getting information on the System.Windows.Forms.Form class is shown in Example 1-25.

Example 1-25. Correct usage of GetType()

C# (GetType)

 ...                 theType = Type.GetType(                     "System.Windows.Forms.Form , " +                     "System.Windows.Forms, " +                     "Version=1.0.5000.0, " +                     "Culture=neutral, " +                     "PublicKeyToken=b77a5c561934e089",                     true); 

VB.NET (GetType)

 ...             theType = Type.GetType( _              "System.Windows.Forms.Form , " & _                 "System.Windows.Forms, " & _                 "Version=1.0.5000.0, " & _                 "Culture=neutral, " & _                 "PublicKeyToken=b77a5c561934e089", _              True) 

In Example 1-25, the class name System.Windows.Forms.Form is followed (comma separated) by the name of the assembly in which that class is located (System.Windows.Forms), the version number (1.0.5000.0), the culture (neuTRal), and the public key token of that assembly (b77a5c561934e089). The public key token for an assembly can be obtained by using the sn (strong name) tool. Only if you specify the fully qualified class name along with the assembly identity will you correctly retrieve the type information. As long as the application can locate the specified assembly, the type information will be loaded.

How does the application find the assembly? First the CLR looks for it in the Global Assembly Cache (GAC). Then it looks in the application base directory. If the assembly is still not found, it searches for subdirectories with the assembly name or the specific culture under the application base directory. If it still hasn't found the assembly, it continues looking for directories specified in the codebase setting in the application's configuration file. You can also explicitly load the assembly using the LoadFrom() method of the System.Reflection.Assembly class.

If you are using some API or library that expects you to send the name of a class, you have to follow the recommendations in this gotcha. If the class name is specified in a configuration file or is given as input for the program during runtime, you must make sure that the fully qualified name as discussed above is provided.

IN A NUTSHELL

When specifying class names in a configuration file or providing them as input for dynamically creating objects, make sure you provide the fully qualified class name, along with the full identity of the assembly.

SEE ALSO

Gotcha #11, "Public key reported by sn.exe is inconsistent" and Gotcha #14, "Type.GetType() might fail at run-time"



    .NET Gotachas
    .NET Gotachas
    ISBN: N/A
    EAN: N/A
    Year: 2005
    Pages: 126

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net