Discovering Type Information

Summary

Reflection Overview

The classes in the Reflection namespace together with System.Type allow you to obtain information about loaded assemblies and the types defined within them. The reflection process provides objects that encapsulate assemblies, modules, and types. Reflection can also be used to dynamically create an instance of a type and then invoke its methods and properties. Typical uses of reflection include the following:

Reflection has many uses. For example, reflection is sometimes used to create object browsers similar to the VisualStudio.NET object browser. Reflection is also used by System.Runtime.Serialization uses reflection to determine which fields should be serialized.

Browsing Type Information

System.Type is the root of all reflection operations. It is also the object that represents a type inside the CLR. System.Type is the primary way to access metadata via the reflection API. Use the members of System.Type to get all required information about a type declaration such constructors, fields, methods, properties, events, as well as the module or assembly in which the class is deployed. So how do you get a System.Type object?

The following code shows what is usually the starting point for getting a System.Type object:

private void btnGetType_Click(object sender, System.EventArgs e)
{
    // Get the assembly associated with the System.Diagnostics.Trace class
    Assembly a = typeof(System.Diagnostics.Trace).Assembly;

    // Get all types present in assembly 'a' and display them
    System.Type[] aTypes = a.GetTypes();
    foreach (Type type in aTypes )
        Trace.WriteLine( "Type: " + type.Name );
}

      

After you obtain a System.Type object, you can discover information about the members of that type using any of the GetX function members (GetMethod, GetEvent, GetField, GetConstructor, etc. ). With a System.Type object you can go backwards in the sense of discovering the containing assembly or module.

The following code show how to use a System.Type object to discover methods exposed on that type:

private void btnGetX_Click(object sender, System.EventArgs e)
{
    // Get the Trace type from System.Diagnostics assembly
    System.Type typTrace = typeof(System.Diagnostics.Trace);

    // Now see what methods are supported in the Trace type
    foreach( MethodInfo mi in typTrace.GetMethods() )
    {
        StringBuilder sb = new StringBuilder();
        sb.Append( mi.ReturnType.Name );
        sb.Append( " " );
        sb.Append( mi.Name );
        Trace.WriteLine( sb.ToString() );
    }
}

      

Reflection Patterns

The most commonly methods in System.Reflection use a consistent pattern to discover type information. Members of Module, Type, and MemberInfo use the following patterns for functions that discover type information:

FieldInfo fi = myType.GetField( "strFirstName", BindingFlags.Public | BindingFlags.Instance );

FieldInfo[] aFI = myType.GetFields();

FieldInfo[] aFI = myType.GetFields( BindingFlags.Public | BindingFlags.Instance );

MemberInfo[] aMI = myType.FindMembers( MemberTypes.Constructor, ... );

Security Considerations

The ability to discover information about an assembly and call its methods via reflection (even the private ones) presents security risks. .Therefore, NET Framework security can be used to enforce rules that determine to what degree reflection can be used to discover type information and access types. Depending on the operator being performed a ReflectionPermission or SecurityPermission might be required. The following tasks do not require permissions:

The following tasks require ReflectionPermissions:

Dynamically Loading and Using Types

Reflection provides the ability to perform late binding. Binding is the process of locating a type's declaration. When this process occurs at run-time rather than at compile-time, it is called later binding. Reflection can be used to explicitly perform late binding. In other words, Reflection can be used to locate a type' declaration, create an instance of that type and then invoke methods on that instance. The following example shows how to create a DirectoryInfo object and invoke the GetDirectories() method on it:

private void btnBinding_Click(object sender, System.EventArgs e)
{
    // Get the System.IO.DirectoryInfo type
    System.Type type = typeof( System.IO.DirectoryInfo );

    // Instantiate a DirectoryInfo class using the constructor that takes a single argument
    DirectoryInfo di = (DirectoryInfo)System.Activator.CreateInstance( type, new object[] {@"C:\"} );

    // Get the GetDirectories method and invoke it. Note there are two overloads of GetDirectories,
    // one that takes no parameters and another that takes a single parameter. We want the 
    // parameterless GetDirectories - note how an empty Type[] is used to specify no parameters
    MethodInfo mi = type.GetMethod( "GetDirectories", new Type[] {});

    // Now invoke GetDirectories. Note how the second parameter specifies no parameters for this method
    DirectoryInfo[] aDI = (DirectoryInfo[])mi.Invoke( di, null );
    foreach (DirectoryInfo diOb in aDI )
    {
        Trace.WriteLine( diOb.FullName );
    } 
}

Accessing Default Members

Any type can have a default member, which is a member that is invoked when no member name is given. Default members are marked with System.Reflection.DefaultMemberAttribute:

public class MyClass
{
     
    [DefaultMember("foo")]
    private void foo() { ... }

}

To retrieve a default member via reflection, you need to use the DefaultMemberAttribute class as follows:

Type t = typeof( DefaultMemberAttribute );
DefaultMemberAttribute dma = (DefaultMemberAttribute)Attribute.GetCustomAttribute( Assembly.GetAssembly( t ), t );
MemberInfo[] aMI = t.GetMember( dma.MemberName );

Another approach would be to use Type.GetDefaultMembers() which gives exactly the same results.

Accessing Custom Attributes

Reflection is also used to query custom attributes associated with program elements. The main reflection methods to query for custom attributes are: