Skip to Content
All posts

Mastering Reflection in C#

Accessing Metadata - An In-Depth Dive for Software Engineers

2 min read ·  — #csharp-interview#middle-specialist#reflection

Mastering Reflection in C#

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.

Further Reading: