C# Reflection
Reflection is the ability of a program to inspect itself at runtime — to examine the types, methods, properties, and attributes in its own code, and even invoke them dynamically. It is how frameworks like ASP.NET, Entity Framework, and unit test runners work internally.
What Reflection Can Do
┌──────────────────────────────────────────────────────────────┐ │ With Reflection you can, at runtime: │ │ │ │ ✅ Get the type name of any object │ │ ✅ List all methods, properties, and fields of a class │ │ ✅ Read custom attributes applied to members │ │ ✅ Create an instance of a class by name (as a string) │ │ ✅ Call a method dynamically (without knowing it at design) │ │ ✅ Get or set property values dynamically │ └──────────────────────────────────────────────────────────────┘
The Type Class
The entry point for reflection is the Type class. It describes every type in .NET.
using System;
using System.Reflection;
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
public void Greet()
{
Console.WriteLine($"Hi, I'm {Name}, age {Age}");
}
}
class Program
{
static void Main()
{
// Three ways to get a Type object:
Type t1 = typeof(Person); // from type name at compile time
Type t2 = new Person("Alice", 30).GetType(); // from object instance
Type t3 = Type.GetType("Person"); // from string name
Console.WriteLine(t1.Name); // Person
Console.WriteLine(t1.FullName); // YourNamespace.Person
Console.WriteLine(t1.IsClass); // True
Console.WriteLine(t1.Namespace); // YourNamespace
}
}
Listing Members with Reflection
Type type = typeof(Person);
Console.WriteLine("=== Methods ===");
foreach (MethodInfo method in type.GetMethods())
{
Console.WriteLine(method.Name);
}
// GetHashCode, Equals, ToString, Greet, get_Name, set_Name, get_Age, set_Age
Console.WriteLine("=== Properties ===");
foreach (PropertyInfo prop in type.GetProperties())
{
Console.WriteLine(prop.Name + " : " + prop.PropertyType.Name);
}
// Name : String
// Age : Int32
Console.WriteLine("=== Constructors ===");
foreach (ConstructorInfo ctor in type.GetConstructors())
{
Console.WriteLine(ctor.ToString());
}
Creating Instances Dynamically
You can create an object at runtime using Activator.CreateInstance() — even when you do not know the type at compile time.
// Create an instance without 'new Person(...)':
object obj = Activator.CreateInstance(typeof(Person), "Bob", 25);
// Cast and use:
Person p = (Person)obj;
p.Greet(); // Hi, I'm Bob, age 25
// From a string type name (useful in plugin systems):
Type type = Type.GetType("YourNamespace.Person");
object obj2 = Activator.CreateInstance(type, "Carol", 28);
Dynamic Instance Diagram
┌──────────────────────────────────────────────────────────────┐
│ Normal: │
│ Person p = new Person("Alice", 30); ← compile-time │
│ │
│ Reflection: │
│ string typeName = "Person"; ← could come from DB │
│ Type t = Type.GetType(typeName); │
│ object p = Activator.CreateInstance(t, "Alice", 30); │
│ ↑ │
│ type decided at runtime │
└──────────────────────────────────────────────────────────────┘
Calling Methods Dynamically
Person person = new Person("Dave", 35);
Type type = typeof(Person);
// Get the method:
MethodInfo greetMethod = type.GetMethod("Greet");
// Invoke it on the object:
greetMethod.Invoke(person, null); // Hi, I'm Dave, age 35
// null = no parameters needed for Greet()
// Method with parameters:
MethodInfo setName = type.GetMethod("set_Name"); // property setter
setName.Invoke(person, new object[] { "Eve" });
Console.WriteLine(person.Name); // Eve
Reading and Setting Properties Dynamically
Person person = new Person("Frank", 40);
Type type = typeof(Person);
// Read a property value:
PropertyInfo nameProp = type.GetProperty("Name");
string nameValue = (string)nameProp.GetValue(person);
Console.WriteLine(nameValue); // Frank
// Set a property value:
nameProp.SetValue(person, "Grace");
Console.WriteLine(person.Name); // Grace
// Loop through all properties and print their values:
foreach (PropertyInfo prop in type.GetProperties())
{
Console.WriteLine(prop.Name + " = " + prop.GetValue(person));
}
// Name = Grace
// Age = 40
Reading Attributes via Reflection
[Obsolete("Use NewMethod instead.")]
class OldApi
{
[Obsolete("Deprecated.")]
public void OldMethod() { }
}
class Program
{
static void Main()
{
Type type = typeof(OldApi);
ObsoleteAttribute attr = (ObsoleteAttribute)Attribute
.GetCustomAttribute(type, typeof(ObsoleteAttribute));
if (attr != null)
Console.WriteLine("Obsolete message: " + attr.Message);
// Obsolete message: Use NewMethod instead.
}
}
Reflection Use Cases in Real Frameworks
┌────────────────────────────────────────────────────────────┐ │ Framework │ How It Uses Reflection │ ├────────────────────────────────────────────────────────────┤ │ Unit test runners │ Find methods with [Test] attribute │ │ ASP.NET Core │ Find controllers and [HttpGet] routes│ │ Entity Framework │ Map properties to DB columns │ │ JSON serializer │ Read/write all public properties │ │ Dependency inject │ Create instances by registered type │ │ Plugin systems │ Load and invoke types from DLL files │ └────────────────────────────────────────────────────────────┘
Reflection Performance Note
┌──────────────────────────────────────────────────────────────┐ │ Reflection is slower than direct code. │ │ │ │ Normal method call: very fast │ │ Reflection method call: 10-100x slower │ │ │ │ Use reflection for: │ │ ✅ Framework/tooling code that runs once at startup │ │ ✅ Plugin and dynamic loading scenarios │ │ ✅ Serialization, testing, and debugging │ │ │ │ Avoid reflection for: │ │ ❌ Hot code paths called millions of times per second │ │ ❌ Performance-critical inner loops │ └──────────────────────────────────────────────────────────────┘
Quick Summary
┌──────────────────────────────────────────────────────────────┐
│ typeof(T) → get Type object at compile time │
│ obj.GetType() → get Type at runtime │
│ Type.GetType("Name") → get Type from string name │
│ │
│ type.GetMethods() → list all methods │
│ type.GetProperties() → list all properties │
│ type.GetFields() → list all fields │
│ │
│ Activator.CreateInstance(type, args) → create object │
│ method.Invoke(obj, args) → call method │
│ prop.GetValue(obj) → read property │
│ prop.SetValue(obj, value) → set property │
│ │
│ Attribute.GetCustomAttribute(member, attrType) │
│ → read attribute │
└──────────────────────────────────────────────────────────────┘
Reflection is a powerful advanced tool that makes C# frameworks flexible and extensible. Understanding it demystifies how popular libraries like ASP.NET, Entity Framework, and NUnit work behind the scenes. Use it intentionally and sparingly — its power comes with a performance trade-off.
