Reflection & Dynamic Type Inspection
Lesson 19 โข Advanced Track
What You'll Learn
- Inspect types, properties, methods, and fields at runtime using System.Reflection
- Create instances dynamically with Activator.CreateInstance
- Invoke methods and access properties by name at runtime
- Define and apply custom attributes to classes, methods, and properties
- Read custom attributes via reflection for metadata-driven architectures
- Understand when reflection is appropriate and its performance trade-offs
๐ก Real-World Analogy
Reflection is like being able to X-ray a machine while it's running. Normally you interact with a car by turning the steering wheel and pressing pedals (public API). With reflection, you can open the hood, see every component, read serial numbers, and even rewire connections โ all while the engine is running. Frameworks like ASP.NET use this to discover your controllers, and testing tools use it to access private members.
1. Type Inspection
Every type in .NET has a Type object that describes its structure. Use typeof(T) at compile time or obj.GetType() at runtime to get it. From there, enumerate properties, methods, fields, and constructors โ even private ones.
Type Inspection
Inspect properties, methods, and fields of a class using reflection.
using System;
using System.Reflection;
class Person
{
public string Name { get; set; } = "";
public int Age { get; set; }
private string secret = "hidden";
public void Greet() => Console.WriteLine($"Hi, I'm {Name}!");
private void Think() => Console.WriteLine("Thinking...");
public string GetInfo(string prefix) => $"{prefix}: {Name}, age {Age}";
}
class Program
{
static void Main()
{
Type type = typeof(Person);
Console.WriteLine($"=== Type: {t
...2. Dynamic Invocation
Create objects and call methods by name without compile-time references. Activator.CreateInstance instantiates types dynamically. MethodInfo.Invoke calls methods with parameters. This powers plugin systems where types are discovered at runtime.
Dynamic Invocation
Create objects and invoke methods dynamically by name.
using System;
using System.Reflection;
class Calculator
{
public int Add(int a, int b) => a + b;
public int Multiply(int a, int b) => a * b;
public string Describe() => "I'm a Calculator!";
}
class Program
{
static void Main()
{
// Create instance dynamically (no 'new Calculator()')
Type calcType = typeof(Calculator);
object? calc = Activator.CreateInstance(calcType);
Console.WriteLine("=== Dynamic Method Invocation ===");
// Invoke
...3. Custom Attributes
Attributes are metadata tags you attach to code. Define your own by extending Attribute, then read them via reflection. This is how ASP.NET discovers routes ([HttpGet]), EF maps properties ([Required]), and serializers control output ([JsonIgnore]).
Custom Attributes
Define attributes, apply them, and read them with reflection.
using System;
using System.Reflection;
// Define a custom attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
class ApiEndpointAttribute : Attribute
{
public string Route { get; }
public string Method { get; }
public ApiEndpointAttribute(string route, string method = "GET")
{
Route = route;
Method = method;
}
}
[AttributeUsage(AttributeTargets.Property)]
class RequiredAttribute : Attribute { }
// Apply attrib
...Pro Tips
- ๐ก Cache reflection results:
typeof(T).GetProperties()is slow. Store the result in a static field if you call it repeatedly. - ๐ก Use source generators over reflection: In .NET 6+, source generators produce compile-time code that's faster than runtime reflection.
- ๐ก BindingFlags control visibility: Combine
Public | NonPublic | Instance | Staticto find exactly what you need. - ๐ก Reflection breaks at refactoring:
GetMethod("Add")won't update if you rename the method. Usenameof(Add)where possible.
Common Mistakes
- Performance in hot paths: Reflection is 10-100x slower than direct calls. Never use it in tight loops or performance-critical code.
- Missing BindingFlags:
GetMethod("Think")returns null for private methods unless you includeBindingFlags.NonPublic. - Wrong parameter types in Invoke:
method.Invoke(obj, new object[] {"5", "3"})passes strings when the method expects ints โ runtime crash. - Trimming breaks reflection: .NET's publish trimmer removes "unused" types. If you only access types via reflection, they get trimmed. Use
[DynamicallyAccessedMembers].
๐ Lesson Complete
- โ
typeof(T)andGetType()give you theTypemetadata object - โ Inspect properties, methods, fields โ including private members with BindingFlags
- โ
Activator.CreateInstancecreates objects dynamically withoutnew - โ
MethodInfo.Invokecalls methods by name at runtime - โ
Custom attributes add metadata; read them with
GetCustomAttribute - โ Reflection is powerful but slow โ cache results and prefer source generators
- โ Next lesson: Generics โ constraints, variance, and advanced patterns
Sign up for free to track which lessons you've completed and get learning reminders.