The Necessity of Type Metadata
The ability to fully describe types (classes,interfaces,structures,enumerations and delegates) using metadata is a key element of the .NET Platform.
Understanding Reflection
In the .NET Universe, reflection the process of runtime type discovery. Using reflection services, you can programmatically obtain the same metadata information generated by ildasm.exe using a friendly object model. For example, you can obtain a list of all types contained within a given *.dll or *.exe assembly, including the methods,fields,properties, and events defined by a given type.
System.Reflection contains several related types.
Members
- Assembly: This abstract class contains members that allow you to load, investigate, and manipulate an assembly.
- AssemblyName: This class allows you to discover numerous details behind an assembly's identity (version info, culture info, etc.)
- EventInfo : This abstract class holds information for a given event.
- FieldInfo : This abstract class holds information for a given field.
- MemberInfo : This is the abstract base class that defines common behaviors for the EventInfo, FieldInfo, MethodInfo, and PropertyInfo types.
- MethodInfo : This abstract class contains information for a given method.
- Module : This abstract class allows you to access a given module within a multifile assembly.
- ParameterInfo : This class holds information for a given parameter.
- PropertyInfo : This abstract class holds information for a given property.
The System.Type Class
This class defines members that can be used to examine a type's metadata, a great number of which return types from the System.Reflection namespace. For ex;
Type.GetMethods() returns an array of the MethodInfo objects,
Type.GetFields() returns an array of FieldInfo objects, and so on.
Member
IsAbstract
IsArray
IsClass
IsCOMObject
IsEnum
IsGenericTypeDefinition
IsValueType
IsGenericParameter
IsInterface
IsPrimitive
IsNestedPrivate
IsNestedPublic
IsSealed
These properties allow you to discover a number of basic traits about the type you are referring to.
GetConstructors()
GetMembers()
GetEvents()
GetMethods()
GetFields()
GetNestedTypes()
GetInterfaces()
GetProperties()
These methods allow you to obtain an array representing the items you are interested in. Each method returns a related array (e.g. GetFields() returns a FieldInfo array, etc.)
FindMembers() : This method returns a MemberInfo array based on search criteria.
GetType() : This static method returns a Type instance given a string name.
InvokeMember() : This method allows "late binding" for a given item.
Obtaining a Type Reference Using System.Object.GetType()
You can obtain an instance of the Type class in a variety of ways. However, the on thing you cannot do is directly create a Type object using the new keyword, as Type is an abstract class.
SportsCar sc = new SportsCar();
Type t = sc.GetType();
Obtaining a Type Reference Using typeof()
Type t = typeof(SportsCar);
Obtaining a Type Reference Using System.Type.GetType()
To obtain type information in a more flexible manner, you may call the static GetType() member of the System.Type class and specify the fully qualified string name of the type you are interested in examining.
Using this approach, you do not need to have compile-time knowledge of the type you are extracting metadata from, given that Type.GetType() takes an instance of the omnipresent System.String.
The Type.GetType() method has been overloaded to allow you to specify 2 Boolean parameters, one of which controls whether an exception should be thrown if the type cannot be found, and the other of which establishes the case sensitivity of the string.
Type t = Type.GetType("CarLibrary.SportsCar",false,true);
In this case, the assumption is that the type is defined within the currently executing assembly. However, you want to obtain metadata for a type within an external assembly, the string parameter is formatted using the type's fully qualified name, followed by a comma, followed by the assembly name of the assembly containing type:
Type t = Type.GetType("CarLibrary.SportsCar, CarLibrary");
Do know that the string passed into Type.GetType() may specify a plus token (+) to denote a nested type.
Type t = Type.GetType("CarLibrary.JamesBondCar+SpyOptions");
Building a Custom Metadata Viewer
using System.Reflection;
Reflecting on Methods
static void ListMethods(Type t)
{
MethodInfo[] mi = t.GetMethods();
foreach(MethodInfo m in mi)
{
Console.WriteLine(m.Name);
}
}
You can use LINQ queries:
var mi = from n in t.GetMethods() orderby n.Name select n.Name;
var mi2 = t.GetMethods().OrderBy(m => m.Name);
Reflecting on Fields and Properties
static void ListFields(Type t)
{
FieldInfo[] fi = t.GetFields();
foreach (FieldInfo f in fi)
{
Console.WriteLine(f.Name);
}
}IN LINQ:
var fi = from f in t.GetFields() orderby f.Name select f.Name;
var fi2 = t.GetFields().OrderBy(f => f.Name).Select(f => f.Name);static void ListProps(Type t)
{
PropertyInfo[] pi = t.GetProperties();
foreach (PropertyInfo p in pi)
{
Console.WriteLine(p.Name);
}
var pi2 = from p in t.GetProperties() orderby p.Name select p;
var pi3 = t.GetProperties().OrderBy(p => p.Name).Select(p => p.Name);
}IN LINQ:
var pi2 = from p in t.GetProperties() orderby p.Name select p;
var pi3 = t.GetProperties().OrderBy(p => p.Name).Select(p => p.Name);
Reflecting on Implemented Interfaces
The only point of interest here is that the call to GetInterfaces() returns an array of System.Types!
static void ListInterfaces(Type t)
{
Type[] ifaces = t.GetInterfaces();
foreach(Type i in ifaces)
{
Console.WriteLine(i.Name);
}
}
IN LINQ:
var ifaces = t.GetInterfaces().OrderBy(i => i.Name).Select(i => i.Name);
Displaying Various Odds and Ends
static void ListVariuosStats(Type t)
{
Console.WriteLine("Base class is : "+t.BaseType);
Console.WriteLine("Is type abstract : "+t.IsAbstract);
Console.WriteLine("Is type sealed : "+t.IsSealed);
Console.WriteLine("Is type generic : "+t.IsGenericTypeDefinition);
Console.WriteLine("Is type a class type:"+t.IsClass);
}
Adding Top-Level Statements
string typeName = Console.ReadLine(); // text : System.Int32
try
{
Type t = Type.GetType(typeName);
ListVariuosStats(t);
ListFields(t);
ListProps(t);
ListMethods(t);
ListInterfaces(t);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Reflecting on Static Types
If you enter System.Console, an exception will be thrown in the first helper method because the value for t will be null. Static types cannot be loaded using the Type.GetType(typeName) method.
Instead, you must use another mechanism, the typeof function from System.Type.
Type t = Type.GetType(typeName);
if (t == null && typeName.Equals("System.Console", StringComparison.OrdinalIgnoreCase))
{
t = typeof(System.Console);
}
Reflecting on Generic Types
You must make use of a special syntax involving a "backtick" character (`) followed by a numerical value that represents the number of type parameters the type supports.
System.Collections.Generic.List`1
System.Collections.Generic.Dictionary`2
Reflecting on Method Parameters and Return Values
MethodInfo[] mi = t.GetMethods().OrderBy(x => x.Name).ToArray();
foreach (MethodInfo m in mi)
{
string retVal = m.ReturnType.FullName;
string paramInfo = "( ";
foreach(ParameterInfo pi in m.GetParameters())
{
paramInfo += pi.ParameterType + " " + pi.Name;
}
paramInfo += " )";
Console.WriteLine(retVal+" "+m.Name+" "+paramInfo); // System.Double Abs ( System.Double Value )
}