Flylib.com

Books Software

 
 
 

3.11 Test an Object s Type


3.11 Test an Object's Type

Problem

You need to test the type of an object.

Solution

Use the inherited Object.GetType method to obtain a Type for the object. In some situations, you can also use the is and as operators to test an object's type.

Discussion

All types inherit the GetType method from the Object base class. As discussed in recipe 3.10, this method returns a Type reference representing the type of the object. The runtime maintains a single instance of Type for each type loaded and all references for this type refer to this same object. This means that you can compare two type references efficiently . The IsStringReader method shown here demonstrates how to test if an object is a System.IO.StringReader .

// Create a new StringReader for testing.
Object someObject = 
    new StringReader("This is a StringReader");

// Test if someObject is a StringReader by obtaining and 
// comparing a Type reference using the typeof operator.
if (typeof(System.IO.StringReader) == someObject.GetType()) {
    // Do something
    
}

C# provides the is operator as a quick way to perform the same test as the IsStringReader method. In addition, is will return true if the tested object is derived from the specified class. This code fragment tests if someObject is an instance of System.IO.TextReader , or a derived class (such as StringReader ).

// Test if someObject is, or is derived from, a TextReader
// using the is operator.
if (someObject is System.IO.TextReader) {
    // Do something
    
}

Both of these approaches require that the type used with the typeof and is operators be known and resolvable at compile time. A more flexible (but slower) alternative is to use the Type.GetType method to return a Type reference for a named type. The Type reference isn't resolved until run time, which causes the performance hit, but allows you to change the type comparison at run time based on the value of a string. The IsType method here returns true if an object is of a named type and uses the Type.IsSubclassOf method to test if the object is a subclass of the named type.

public static bool IsType(object obj, string type) {

    // Get the named type, use case insensitive search, throw 
    // an exception if the type is not found.
    Type t = Type.GetType(type, true, true);

    return t == obj.GetType()  obj.GetType().IsSubclassOf(t);
}

Finally, you can use the as operator to perform a safe cast of any object to a specified type. If the object can't be cast to the specified type, the as operator returns null . This allows you to perform safe casts that are easy to verify, but the compared type must be resolvable at run time. Here's an example:

// Use the "as" operator to perform a safe cast.
StringReader reader = someObject as System.IO.StringReader;
if (reader != null) {
    // Do something with reader
    
}
Tip  

The static method GetUnderlyingType of the System.Enum class allows you to retrieve the underlying type of an enumeration.



3.12 Instantiate an Object Using Reflection

Problem

You need to instantiate an object at run time using reflection.

Solution

Obtain a Type object representing the type of object you want to instantiate, call its GetConstructor method to obtain a System.Reflection.ConstructorInfo object representing the constructor you want to use, and execute the ConstructorInfo.Invoke method.

Discussion

The first step in creating an object using reflection is to obtain a Type object that represents the type you want to instantiate. (See recipe 3.10 for details.) Once you have a Type instance, call its GetConstructor method to obtain a ConstructorInfo representing one of the type's constructors. The most commonly used overload of the GetConstructor method takes a Type array argument and returns a ConstructorInfo representing the constructor that takes the number, order, and type of arguments specified in the Type array. To obtain a ConstructorInfo representing a parameterless (default) constructor, pass an empty Type array (use the static field Type.EmptyTypes ); don't use null , or GetConstructor will throw a System.ArgumentNullException . If GetConstructor can't find a constructor with a signature that matches the specified arguments, it will return null .

Once you have the desired ConstructorInfo , call its Invoke method. You must provide an object array containing the arguments you want to pass to the constructor. Invoke instantiates the new object and returns an object reference to it, which you must cast to the appropriate type. The following code demonstrates how to instantiate a System.Text.StringBuilder object, specifying the initial content for the StringBuilder (a string ) and its capacity (an int ).

// Obtain the Type for the StringBuilder class.
Type type = typeof(System.Text.StringBuilder);

// Create a Type[] containing Type instances for each
// of the constructor arguments - a string and an int.
Type[] argTypes = new Type[] {typeof(System.String), typeof(System.Int32)};

// Obtain the ConstructorInfo object.
ConstructorInfo cInfo = type.GetConstructor(argTypes);

// Create an object[] containing the constructor arguments.
object[] argVals = new object[] {"Some string", 30};

// Create the object and cast it to StringBuilder.
StringBuilder sb = (StringBuilder)cInfo.Invoke(argVals);

Reflection functionality is commonly used to implement factories in which you use reflection to instantiate concrete classes that either extend a common base class or implement a common interface. Often both an interface and a common base class are used. The abstract base class implements the interface and any common functionality, and then each concrete implementation extends the base class.

There's no mechanism to formally declare that each concrete class must implement constructors with specific signatures. If you intend third parties to implement concrete classes, your documentation must specify the constructor signature called by your factory. A common approach to avoid this problem is to use a default (empty) constructor and configure the object after instantiation using properties and methods . The following code demonstrates a factory to instantiate objects that implement the IPlugin interface (first used in recipe 3.7).

using System;
using System.Reflection;

// A common interface that all plug-ins must implement.
public interface IPlugin {
    string Description { get; set; }
    void Start();
    void Stop();
}

// An abstract base class from which all plug-ins must derive.
public abstract class AbstractPlugin : IPlugin {

    // Hold a description for the plug-in instance
    private string description = "";

    // Sealed property to get the plug-in description.
    public string Description { 
        get { return description; }
        set { description = value; }
    }

    // Declare the members of the IPlugin interface as abstract.
    public abstract void Start();
    public abstract void Stop();
}

// A simple IPlugin implementation to demonstrate the PluginFactory class.
public class SimplePlugin : AbstractPlugin {

    // Implement Start method.
    public override void Start() {
        Console.WriteLine(Description  + ": Starting...");
    }

    // Implement Stop method.
    public override void Stop() {
        Console.WriteLine(Description + ": Stopping...");
    }
}

// A factory to instantiate instances of IPlugin.
public sealed class PluginFactory {

    public static IPlugin CreatePlugin(string assembly, 
        string pluginName, string description) {

        // Obtain the Type for the specified plug-in.
        Type type = Type.GetType(pluginName + ", " + assembly);

        // Obtain the ConstructorInfo object.
        ConstructorInfo cInfo = type.GetConstructor(Type.EmptyTypes);

        // Create the object and cast it to StringBuilder.
        IPlugin plugin = (IPlugin)cInfo.Invoke(null);

        // Configure the new IPlugin
        plugin.Description = description;

        return plugin;
    }
}

This statement will create an instance of SimplePlugin using the PluginFactory class.

IPlugin plugin = PluginFactory.CreatePlugin(
    "CreateObjectExample",  // Private assembly name
    "SimplePlugin",         // Plug-in class name
    "A Simple Plugin"       // Plug-in instance description
);
Note  

The System.Activator class provides two static methods named CreateInstance and CreateInstanceFrom that instantiate objects based on Type objects or strings containing type names . See the .NET Framework SDK documentation for more details.