Mastering Reflection in C#
Accessing Metadata - An In-Depth Dive for Software Engineers
2 min read · — #csharp-interview#middle-specialist#reflection
Reflection is a cornerstone of dynamic programming in C#. For software engineers, leveraging this feature effectively can open doors to advanced functionalities. However, it's essential to understand its intricacies.
1. Viewing Type Information:
The heart of reflection is the Type
class, which represents type declarations.
Example:
class MyClass
{
public int MyField;
public void MyMethod() { }
public event EventHandler MyEvent;
}
Type typeInfo = typeof(MyClass);
// Constructors
foreach (var ctor in typeInfo.GetConstructors())
Console.WriteLine(ctor);
// Similarly for methods, fields, events, etc.
2. Reflection and Generic Types:
Generics introduce nuances. The difference between generic type definitions and constructed types becomes pivotal.
Example:
public class GenericClass<T> { public T Value; }
Type genericDef = typeof(GenericClass<>);
Type constructed = typeof(GenericClass<int>);
3. Security Considerations for Reflection:
Beware! Reflection can potentially breach encapsulation. .NET Framework often sets security barriers to prevent unintended access.
4. Dynamically Loading and Using Types:
Reflection's dynamic type loading aids in plugin-based architectures and more.
Example:
Assembly assembly = Assembly.LoadFrom("MyAssembly.dll");
Type myType = assembly.GetType("MyNamespace.MyType");
object instance = Activator.CreateInstance(myType);
5. Reflection-Only Context:
Load assemblies without executing their code.
Example:
Assembly reflectionOnlyAssembly =
Assembly.ReflectionOnlyLoadFrom("MyAssembly.dll");
Console.WriteLine(reflectionOnlyAssembly.FullName);
6. MetadataLoadContext:
This is a boon for .NET Core developers, allowing isolated metadata inspection.
7. Accessing Custom Attributes:
Reflection shines when reading custom metadata from code elements.
Example:
[AttributeUsage(AttributeTargets.Class)]
public class MyAttribute : Attribute { /*...*/ }
// Use Reflection to read the attribute
8. Specifying Fully Qualified Type Names:
Resolve types with precision.
Example:
Type type = Type.GetType("System.String, mscorlib, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089");
9. Hook up a Delegate Using Reflection:
This showcases Reflection's power, enabling dynamic event subscription.
Example:
public class EventHolder { /*...*/ }
public class Subscriber { /*...*/ }
EventHolder holder = new EventHolder();
Subscriber subscriber = new Subscriber();
EventInfo eventInfo = holder.GetType().GetEvent("MyEvent");
MethodInfo methodInfo = subscriber.GetType().GetMethod("OnMyEvent");
Delegate d = Delegate.CreateDelegate(eventInfo
.EventHandlerType, subscriber, methodInfo);
eventInfo.AddEventHandler(holder, d);
holder.MyEvent?.Invoke(null, EventArgs.Empty);
10. Emitting Dynamic Methods and Assemblies:
Reflection.Emit can dynamically generate IL code.
Example:
AssemblyBuilder assemblyBuilder = AssemblyBuilder
.DefineDynamicAssembly(/*...*/);
// Emit IL, create types, methods, and more.
Performance Considerations:
Reflection is resource-intensive. Invoking methods reflectively is much slower than direct invocations. Ensure you benchmark, especially in performance-critical paths.
Conclusion:
This deep dive into Reflection provides a robust understanding. For engineers, using reflection judiciously is key. Ensure you always weigh the trade-offs between flexibility and performance.