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:
Example: Get the name of the calling routine:
string methodName = new StackTrace().GetFrame(1).GetMethod().Name;
// 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 |
// 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;
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.
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); } } }
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()); } }
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);
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))) { // ... } }
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:
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 } } }