User Tools

Site Tools


notes:csharp:reflection

Reflection in C#

Reflection is an act of inspecting metadata and compiled code at runtime.

All types in C# are represented at runtime with an instance of System.Type. You can obtain a System.Type object by:

  • calling the GetType method on an instance to obtain its type at runtime
  • using the typeof operator on a type name to obtain the type at compile time

GetType and typeof

// Use an instance of a class.
DateTime date = new DateTime(2015, 12, 30);
Type t = date.GetType();
 
// Use the typeof keyword.
Type t = typeof(DateTime);
 
// Use the static method Type.GetType to retrieve a type by name.
Type t = Type.GetType("System.DateTime");
 
// Use the static method Type.GetType to retrieve a type by its assembly qualified name.
// The assembly is loaded behind the scenes.
Type t = 
  Type.GetType("System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
 
// Use a reference to the type's assembly.
// 'WBS' is a namespace; 'TestClass' is a class.
Type t = Assembly.GetExecutingAssembly().GetType("WBS.TestClass");
 
// Obtain array types.
Type t = typeof(int[]);  // one-dimensional array
Type t = typeof(int).MakeArrayType();
Type t = typeof(int[,]); // two-dimensional array
Type t = typeof(int).MakeArrayType(2);
 
// Obtain generic types.
Type t = typeof(Dictionary<,>); // unbound generic type (generic type definition)
Type t = typeof(Dictionary<int, int>); // closed (constructed) generic type
 
// Equivalent expressions to obtain an unbound generic type.
Type t = typeof(List<>);
Type t = Type.GetType("System.Collections.Generic.List`1");
Type t = Type.GetType("System.Collections.Generic.List`1[System.String]").GetGenericTypeDefinition();
 
// Equivalent expressions to obtain a closed generic type.
Type t = typeof(List<string>);
Type t = Type.GetType("System.Collections.Generic.List`1").MakeGenericType(typeof(string));
Type t = Type.GetType("System.Collections.Generic.List`1[System.String]");
 
// Determine if an object is of a given type.
var str = "Test";
Type t = typeof(IComparable);
bool isString = t.IsInstanceOfType(str); // a static equivalent: bool isString = str is IComparable;

Commonly used properties of Type:

Property A value for typeof(String)
Name String
FullName System.String
Namespace System
AssemblyQualifiedName System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
BaseType typeof(Object)
Assembly mscorlib.dll
IsPublic true
IsClass true
IsSealed true
IsAbstract false
IsGenericType false
IsGenericTypeDefinition false

Instantiating types

// Create an instance using the default ctor.
int num = (int)Activator.CreateInstance(typeof(int));
 
// Create an instance using a ctor with parameters.
DateTime date = (DateTime)Activator.CreateInstance(typeof(DateTime), 2015, 12, 30);
 
// Create an instance using a ctor with parameters passed as an array.
object[] args = { 2015, 12, 30 };
DateTime date = (DateTime)Activator.CreateInstance(typeof(DateTime), args);
 
// Create an instance as an interface.
IEnumerable list = Activator.CreateInstance(typeof(List<int>)) as IEnumerable;
 
// Convert an unbound generic type into a closed generic type.
Type t = typeof(List<>).MakeGenericType(typeof(int));
 
// Convert a closed generic type into an unbound generic type.
Type t= typeof(List<int>).GetGenericTypeDefinition();
 
// Obtain the type argument of a closed generic type.
Type t = typeof(bool?).GetGenericArguments()[0]; // System.Boolean
 
// Obtain a placeholder type of an unbound generic type.
Type t = typeof(List<>).GetGenericArguments()[0]; // T

Use ConstructorInfo.Invoke when ctor is ambiguous. For example, TestClass has two ctors and both of them can be invoked using TestClass(null):

// Obtain the TestClass(string str) ctor.
ConstructorInfo ctor = typeof(TestClass).GetConstructor(new[] { typeof(string) }); 
 
// Invoke the TestClass(string str) ctor passing null as the parameter.
object obj = ctor.Invoke(new object[] { null }); 
...
public class TestClass
{
    public TestClass(string str) { }
    public TestClass(StringBuilder str) { }
}

Example: Load an assembly at run-time and instantiate an object (BookRepository) that implements an IBookRepository interface:

// typeName is a fully-qualified type name:
// WBS.Bookstore.BookRepository, WBS.Bookstore, Version=1.0.0.0, Culture=neutral
Type repType = Type.GetType(typeName);
IBookRepository rep = Activator.CreateInstance(repType) as IBookRepository;

Reflecting members

Discover members of a given type:

Type t = typeof(String);
 
// Iterate over all members including inherited ones.
foreach (MemberInfo info in t.GetMembers())
    Console.WriteLine($"Name: {info.Name}, Type: {info.MemberType}");
 
// Iterate over all overloads of a given method and show their parameters.
foreach (MethodInfo info in t.GetMember("Compare"))
{
    Console.WriteLine($"Method: {info}");
    Console.WriteLine("Parameters:");
    foreach (ParameterInfo paramInfo in info.GetParameters())
        Console.WriteLine("   " +
            $"Name: {paramInfo.Name}, " +
            $"Position: {paramInfo.Position}, " +
            $"Type: {paramInfo.ParameterType}");
}
 
// Discover methods, fields, properties, and supported interfaces.
MethodInfo[] methods = t.GetMethods();
FieldInfo[] fields = t.GetFields();
PropertyInfo[] properties = t.GetProperties();
Type[] interfaces = t.GetInterfaces();

The TypeInfo class could be used an alternative to the above methods that return arrays. The properties of TypeInfo return IEnumerable<T>:

Type t = typeof(String);
 
// Iterate over all members excluding inherited ones.
IEnumerable<MemberInfo> members = t.GetTypeInfo().DeclaredMembers;
foreach (MemberInfo info in members)
    Console.WriteLine($"Name: {info.Name}, Type: {info.MemberType}");
 
// Discover methods, fields, and properties.
IEnumerable<MethodInfo> methods = t.GetTypeInfo().DeclaredMethods;
IEnumerable<FieldInfo> fields = t.GetTypeInfo().DeclaredFields;
IEnumerable<PropertyInfo> properties = t.GetTypeInfo().DeclaredProperties;

Use LINQ with a collection obtained from a method of TypeInfo:

// Obtain the parameterless overload of ToString.
IEnumerable<MethodInfo> methods = typeof(Int32).GetTypeInfo().DeclaredMethods;
MethodInfo method = methods.FirstOrDefault(m => m.Name == "ToString" && m.GetParameters().Length == 0);
 
// Check if a class TestClass is annotated with the DataContractAttribute.
var attr = (from a in typeof(TestClass).GetTypeInfo().CustomAttributes
            where a.AttributeType == typeof(DataContractAttribute)
            select a).FirstOrDefault();
...
[DataContract]
class TestClass { }

Use the BindingFlags enum to control how reflection searches for members. The methods GetMembers, GetMethods, GetProperty, GetField, etc. accept a combination of BindingFlags.

  • BindingFlags.Instance - include instance members
  • BindingFlags.NonPublic - include private members
  • BindingFlags.DeclaredOnly - do not include inherited members

Example: Display values of the private integer fields:

TestClass obj = new TestClass(1, 88, "testing");
FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
 
foreach (FieldInfo field in fields)
{
    if (field.FieldType == typeof(Int32))
        Console.WriteLine(field.GetValue(obj)); // 1 88
}
...
class TestClass
{
    private int field1;
    private int field2;
    private string field3;
 
    public TestClass(int f1, int f2, string f3)
    {
        field1 = f1;
        field2 = f2;
        field3 = f3;
    }
}

Example: Use reflection to copy properties from a aook object passed as a parameter to the book object that contains the CopyFrom method:

public class Book
{
    ...
    public void CopyFrom(Book b)
    {
        if (b == null)
            return;
 
        IEnumerable<PropertyInfo> props = typeof(Book).GetTypeInfo().DeclaredProperties;
        foreach (PropertyInfo prop in props)
        {
            object val = prop.GetValue(b);
            prop.SetValue(this, val);
        }
    }
}

Invoking members

Calling methods or accessing properties with a MemberInfo object is called late binding (or dynamic binding). To dynamically call a method, call Invoke on a MethodInfo, providing an array of arguments.

Static binding Dynamic binding
string s = "Barbapapa"; 
int len = s.Length; // 9 
object s = "Barbapapa"; 
PropertyInfo prop = s.GetType().GetProperty("Length");
int len = (int)prop.GetValue(s, null); // 9 
// "papa"
string s = "Barbapapa".Substring(5); 
Type[] paramTypes = { typeof(Int32) };
MethodInfo method = 
    typeof(String).GetMethod("Substring", paramTypes);
 
object[] args = { 5 };
string s = (string)method.Invoke("Barbapapa", args); 
int n;
bool ok = Int32.TryParse("18", out n); 
Type[] paramTypes = 
    { typeof(String), typeof(Int32).MakeByRefType() };
MethodInfo method = 
    typeof(Int32).GetMethod("TryParse", paramTypes);
 
object[] args = { "18", 0 };
bool ok = (bool)method.Invoke(null, args);
int n = (int)args[1]; // 18 
StringBuilder sb = new StringBuilder();
sb.Capacity = 800; 
StringBuilder sb = new StringBuilder();
MethodInfo method = 
    typeof(StringBuilder).GetMethod("set_Capacity");
 
object[] args = { 800 };
method.Invoke(sb, args); 
int a = 77, b = 88;
int result = a.CompareTo(b); // -1 
int a = 77, b = 88;
Type[] paramTypes = { typeof(Int32) };
MethodInfo method = 
    a.GetType().GetMethod("CompareTo", paramTypes);
 
object[] args = { b };
int result = (int)method.Invoke(a, args); // -1 
string[] arr = new string[] { "a", "b", "c" };
int result = TestClass.Search(arr, "a"); 
 
class TestClass
{
    public static int Search<T>(T[] a, T val) 
    {
        // ...
        return 1;
    }
}
string[] arr = new string[] { "a", "b", "c" };
 
MethodInfo method = typeof(TestClass).GetMethod("Search");
MethodInfo generic = 
    method.MakeGenericMethod(typeof(String));
 
int result = 
    (int)generic.Invoke(null, new object[] { arr, "b" }); 

Dynamic binding may be inefficient. You can improve performance by calling a dynamically instantiated delegate that targets the dynamic method.

Example: Call the String.IndexOf method in a tight loop efficiently:

delegate int StringToInt(string s, string sub);
...
MethodInfo method = typeof(String).GetMethod("IndexOf", new Type[] { typeof(String) } ); // bind here
var indexOf = (StringToInt)Delegate.CreateDelegate(typeof(StringToInt), method);
for (int i = 0; i < 100000; ++i)
{
    int index = indexOf("Barbapapa", "papa"); // index == 5
}

Example: Add an event handler OnSubmit to an instance of TestClass:

static void Main(string[] args)
{
    TestClass test = new TestClass();
 
    EventInfo events = typeof(TestClass).GetEvent("Submit");
    if (events != null)
    {
        // Syntax: AddEventHandler (Object target, Delegate handler)
        events.AddEventHandler(test, new EventHandler(OnSubmit));
    }
 
    test.CallSubmit(); // writes to the Console window: "Submit by TestClass"
}
 
private static void OnSubmit(object sender, EventArgs e)
{
    Console.WriteLine("Submit by " + sender.ToString());
}
...
class TestClass
{
    public event EventHandler Submit;
 
    public void CallSubmit()
    {
        if (Submit != null)
            Submit(this, new EventArgs());
    }
}

Reflecting assemblies

You can dynamically reflect an assembly by calling GetType or GetTypes on an Assembly object.

Examples: Using Assembly.LoadFrom:

// List all the top-level types in the assembly C:\BackEnd\DataLayer.dll 
Assembly asm = Assembly.LoadFrom(@"C:\BackEnd\DataLayer.dll");
foreach (Type t in asm.GetTypes())
    Console.WriteLine(t);
 
// List all the top-level types in the assembly C:\BackEnd\DataLayer.dll that implement the IEditable interface.
Assembly asm = Assembly.LoadFrom(@"C:\BackEnd\DataLayer.dll");
var types = from type in asm.GetTypes()
            where typeof(IEditable).IsAssignableFrom(type) && !type.IsInterface
            select type;
foreach (Type t in types)
    Console.WriteLine(t);

Examples: Using AppDomain.CurrentDomain.GetAssemblies:

// List all currently loaded assemblies.
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly a in assemblies)
    Console.WriteLine(a.FullName + Environment.NewLine);
 
// Obtain a list of all static methods available in the current application domain 
// that return the IEnumerable<T> interface.
var query = from assembly in AppDomain.CurrentDomain.GetAssemblies()
            from type in assembly.GetTypes()
            from method in type.GetMethods()
            where method.IsStatic &&
                  method.ReturnType.GetInterface("IEnumerable'1") != null
            orderby method.DeclaringType.Name, method.Name
            group method by
            new
            {
                Class = method.DeclaringType.Name,
                Method = method.Name
            };    

Examples: Using Assembly.ReflectionOnlyLoadFrom:

// Inspect type information in a reflection-only context.
Assembly a = Assembly.ReflectionOnlyLoadFrom(@"C:\BackEnd\DataLayer.dll");
Console.WriteLine(a.ReflectionOnly); // true
foreach (Type t in a.GetTypes())
    Console.WriteLine(t);

Obtaining resources

Example: Read an image from resources:

using (StreamReader reader = new StreamReader(this.GetType().Assembly.GetManifestResourceStream("image.gif")))
{
    System.Drawing.Image image = System.Drawing.Image.FromStream(reader.BaseStream);
    // ... do something with the image
}

Example: Obtain resources from the executing assembly:

Assembly assembly = Assembly.GetExecutingAssembly();
 
// Return the names of resources in the executing assembly.
string[] names = assembly.GetManifestResourceNames();
 
foreach (string name in names)
{
    using (BinaryReader reader = new BinaryReader(assembly.GetManifestResourceStream(name)))
    {
        // ...
    }
}

Attributes

Attributes can be applied to:

  • assemblies
  • types
  • methods
  • parameters
  • properties

Attribute usage constraints enforce correct application of a custom attribute using the AttributeUsage attribute and the AttributeTargets enum. In other words, they define the targets for a custom attribute.

Guidelines for developing custom attributes:

  • A custom attribute class has to derive from System.Attribute (directly or indirectly)
  • A custom attribute name should have a suffix “-Attribute”.
  • Properties of a custom attribute have to be read-write.

Reflecting attributes:

  • Use the Type.GetCustomAttributes method.
  • Use the static members of the Attribute class such as Attribute.IsDefined.

Example: Define a custom attribute TestDescriptionAttribute and apply it to a class TestClass:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
public class TestDescriptionAttribute : System.Attribute
{
    private string description;
    public string Description
    {
        get { return description; }
        set { description = value; }
    }
 
    public TestDescriptionAttribute() { }
 
    // This ctor takes a string as a parameter. As a result we are able to pass a string to
    // our attribute: [TestDescription("string")]
    public TestDescriptionAttribute(string desc)
    {
        description = desc;
    }
}
...
[TestDescription("This is class description.")]
public class TestClass { }

Example: Check if a class TestClass has the Serializable attribute applied:

if (Attribute.IsDefined(typeof(TestClass), typeof(SerializableAttribute)))
{
    // ...
}
...
[Serializable]
class TestClass { }

Example: Obtain all attributes applied to the TestClass that are of type TestAttribute:

IEnumerable<CustomAttributeData> attrs = typeof(TestClass).GetType().GetCustomAttributes(TestAttribute);
...
[Test]
class TestClass { }

Example: Obtain an instance of ConditionalAttribute and examine its ConditionString property:

MethodInfo info = typeof(TestClass).GetMethod("TestMethod");
 
if (Attribute.IsDefined(info, typeof(ConditionalAttribute)))
{
    ConditionalAttribute attr =
        (ConditionalAttribute)Attribute.GetCustomAttribute(info, typeof(ConditionalAttribute));
 
    if (attr != null)
        Console.WriteLine(attr.ConditionString); // DEBUG
}
...
class TestClass
{
    [Conditional("DEBUG")]
    public void TestMethod(string s) { }
}

Example: Obtain assembly-level attributes AssemblyCompanyAttribute and AssemblyTitleAttribute:

string assemblyCompany =
    ((AssemblyCompanyAttribute)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(),
        typeof(AssemblyCompanyAttribute), false)).Company;
string assemblyTitle =
    ((AssemblyTitleAttribute)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(),
        typeof(AssemblyTitleAttribute), false)).Title;

Generics

Retrieve the generic type definition (i.e. the unbound generic type):

// Example #1
class A<T> {}
class A<T1,T2> {}
...
Type a1 = typeof (A<>);   // unbound type (notice no type arguments)
Type a2 = typeof (A<,>);  // use commas to indicate multiple type args
 
// Example #2
Type d = typeof(Dictionary<,>);

Retrieve the closed constructed type:

Type t1 = typeof(List<int>)
Type t2 = typeof(Dictionary<Guid,string>)
Type t3 = typeof(Dictionary<int,T>) // T represents a generic method's type parameter
Type t4 = typeof (A<int,int>);

Retrieve generic and constructed Type objects:

Type t1 = Type.GetType("System.Collections.Generic.List`1");
Type t2 = Type.GetType("System.Collections.Generic.List`1[System.String]");
 
// t3 and t4 are the same as t2.
Type t3 = Type.GetType("System.Collections.Generic.List`1").MakeGenericType(typeof(string)); 
Type t4 = typeof(List<string>);
 
// t5 and t6 are the same as t1.
Type t5 = typeof(List<>);
Type t6 = Type.GetType("System.Collections.Generic.List`1[System.String]").GetGenericTypeDefinition();

Methods and properties of the Type class commonly used with generics:

  • GetGenericTypeDefinition - acts on a constructed type and retrieves the generic type definition
  • MakeGenericType - acts on a generic type definition and returns a constructed type
  • IsGenericType
  • GetGenericArguments
  • IsGenericTypeDefinition

Example: Invoke a static generic method:

using System;
using System.Collections.Generic;
using System.Reflection;
 
namespace TestApp
{
    class MainClass
    {
        public static void Main()
        {
            // Create and populate an array of strings.
            string[] arr = new string[]{ "BBB", "DDD", "AAA", "EEE" };
 
            // Obtain info about the 'BinarySearch' method of the 'MyArray' type.
            MethodInfo method = typeof(MyArray).GetMethod("BinarySearch");
 
            // 'String' is a type parameter of the generic method 'BinarySearch'.
            // You could also write: MethodInfo generic = method.MakeGenericMethod(typeof(String));
            MethodInfo generic = method.MakeGenericMethod(new Type[] {typeof(String)});
 
            // The first parameter is null as we are invoking a static method.
            object result1 = generic.Invoke(null, new object[] { arr, "AAA" }); 
            object result2 = generic.Invoke(null, new object[] { arr, "DDD" });
        }
    }
 
    class MyArray
    {
        public static int BinarySearch<T>(T[] arr, T val) where T : IComparable<T>
        {
            // ... the binary search implementation goes here
        }
    }
}
notes/csharp/reflection.txt · Last modified: 2017/03/06 by admin