C# Reflection
Reflection in C# allows you to inspect and manipulate types, objects, and their members (methods, properties, fields, events, etc.) at runtime. It's a powerful feature that enables dynamic behavior, introspection, and late binding.
What is Reflection?
Reflection is the ability of a program to examine the type or metadata of an object at run time. This allows you to:
- Determine the type of an object.
- Discover the members (methods, properties, fields) of a type.
- Invoke methods dynamically.
- Create instances of types dynamically.
- Access and modify fields and properties at runtime.
Key Types and Namespaces
The core types for reflection are found in the System.Reflection namespace.
Type: Represents type declarations (classes, interfaces, arrays, value types, enum types, type parameters, etc.).MethodInfo: Provides information about a method and access to method metadata.PropertyInfo: Provides information about a property and access to property metadata.FieldInfo: Provides information about a field and access to field metadata.ConstructorInfo: Provides information about a constructor and access to constructor metadata.Assembly: Represents an assembly, which is a module containing a manifest and type definitions and possibly MSIL code.
Getting Type Information
You can obtain a Type object in several ways:
// Using GetType() on an object instance
string myString = "Hello";
Type stringType = myString.GetType();
Console.WriteLine($"Type Name: {stringType.Name}"); // Output: String
// Using typeof() operator for known types
Type intType = typeof(int);
Console.WriteLine($"Type Name: {intType.Name}"); // Output: Int32
// Getting a type from an assembly
// Assembly assembly = Assembly.Load("MyAssembly");
// Type dynamicType = assembly.GetType("MyNamespace.MyClass");
Inspecting Members
Once you have a Type object, you can query its members.
Type personType = typeof(Person); // Assuming a Person class exists
// Get all public methods
MethodInfo[] publicMethods = personType.GetMethods();
Console.WriteLine("Public Methods:");
foreach (var method in publicMethods)
{
Console.WriteLine($"- {method.Name}");
}
// Get a specific public method by name
MethodInfo greetMethod = personType.GetMethod("Greet");
if (greetMethod != null)
{
Console.WriteLine($"Found Greet method: {greetMethod.Name}");
}
// Get all public properties
PropertyInfo[] properties = personType.GetProperties();
Console.WriteLine("\nPublic Properties:");
foreach (var prop in properties)
{
Console.WriteLine($"- {prop.Name} ({prop.PropertyType.Name})");
}
// Get all public fields
FieldInfo[] fields = personType.GetFields(BindingFlags.Public | BindingFlags.Instance);
Console.WriteLine("\nPublic Fields:");
foreach (var field in fields)
{
Console.WriteLine($"- {field.Name} ({field.FieldType.Name})");
}
Invoking Methods and Creating Instances
Reflection allows you to call methods and create objects dynamically.
// Assume Person class with a constructor and a method
// public class Person
// {
// public string Name { get; set; }
// public Person(string name) { Name = name; }
// public void SayHello() { Console.WriteLine($"Hello, my name is {Name}"); }
// }
Type personType = typeof(Person);
// Create an instance using a constructor
object[] constructorArgs = { "Alice" };
object personInstance = personType.GetConstructor(new Type[] { typeof(string) })?.Invoke(constructorArgs);
if (personInstance != null)
{
Console.WriteLine($"Created instance of type: {personInstance.GetType().Name}");
// Invoke a method
MethodInfo sayHelloMethod = personType.GetMethod("SayHello");
sayHelloMethod?.Invoke(personInstance, null); // Invoke with no arguments
}
// Creating an instance without knowing the type at compile time
string typeName = "System.Text.StringBuilder";
Type sbType = Type.GetType(typeName);
if (sbType != null)
{
object sbInstance = Activator.CreateInstance(sbType);
Console.WriteLine($"Created instance using Activator: {sbInstance.GetType().Name}");
}
Use Cases for Reflection
- Serialization/Deserialization: Frameworks like JSON.NET or XML serializers use reflection to discover and process object properties.
- Dependency Injection: IoC containers often use reflection to discover and instantiate services.
- Object-Relational Mappers (ORMs): ORMs use reflection to map database columns to object properties.
- Unit Testing: Reflection can be used to access private members for testing purposes (though this is often discouraged).
- Dynamic Proxies and AOP: Creating dynamic proxies for intercepting method calls.
private). Use this capability with caution and ensure you understand the security implications. The BindingFlags enum is crucial for controlling the scope of member searches.
Conclusion
C# Reflection is a powerful tool for building flexible and dynamic applications. Understanding its capabilities and limitations, particularly regarding performance and security, is essential for effective use.