Reflection in C# — Complete Guide
In this tutorial, you will learn about Reflection in C#. We cover key concepts, practical examples, and best practices to help you master this topic.
Hook
Reflection gives your code the ability to examine itself at runtime. From dependency injection containers to unit test frameworks, many powerful tools in the .NET ecosystem rely on reflection to discover types, invoke methods, and read attributes dynamically.
Learning Path
graph LR A[Reflection] --> B[Type Class] A --> C[Assembly Inspection] B --> D[Member Discovery] B --> E[Dynamic Invocation] D --> F[Custom Attributes] style A fill:#4a90d9,color:#fff style B fill:#4a90d9,color:#fff style C fill:#4a90d9,color:#fff style D fill:#4a90d9,color:#fff style E fill:#4a90d9,color:#fff style F fill:#4a90d9,color:#fff
The Type Class
The Type class is the entry point for reflection. It represents a type in the runtime.
using System;
using System.Reflection;
string name = "Hello";
Type stringType = name.GetType();
Console.WriteLine($"Type: {stringType.FullName}");
// typeof operator
Type intType = typeof(int);
Console.WriteLine($"Is primitive: {intType.IsPrimitive}");
Console.WriteLine($"Is value type: {intType.IsValueType}");
// Get all public properties
PropertyInfo[] props = typeof(DateTime).GetProperties(BindingFlags.Public | BindingFlags.Instance);
Console.WriteLine($"DateTime has {props.Length} public properties");
Output:
Type: System.String
Is primitive: True
Is value type: True
DateTime has 8 public properties
Inspecting Assemblies
Load and inspect assemblies to discover types, methods, and references.
Assembly assembly = Assembly.GetExecutingAssembly();
Console.WriteLine($"Assembly: {assembly.FullName}");
// Get all types
Type[] types = assembly.GetTypes();
Console.WriteLine($"Types: {types.Length}");
// Get referenced assemblies
AssemblyName[] references = assembly.GetReferencedAssemblies();
foreach (var refName in references)
{
Console.WriteLine($" -> {refName.Name}");
}
Output:
Assembly: MyApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Types: 12
-> System.Runtime
-> System.Console
-> System.Linq
Discovering Members
List methods, fields, properties, and constructors of a type.
public class SampleClass
{
public int Id { get; set; }
public string Name { get; set; } = "";
private string _secret = "hidden";
public void DoWork() { }
private void Helper() { }
public static void StaticMethod() { }
}
Type sampleType = typeof(SampleClass);
Console.WriteLine("Methods:");
foreach (MethodInfo method in sampleType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
{
Console.WriteLine($" {method.ReturnType.Name} {method.Name} (IsPublic={method.IsPublic})");
}
Console.WriteLine("Properties:");
foreach (PropertyInfo prop in sampleType.GetProperties())
{
Console.WriteLine($" {prop.PropertyType.Name} {prop.Name}");
}
Output:
Methods:
Void DoWork (IsPublic=True)
Void StaticMethod (IsPublic=True)
Int32 get_Id (IsPublic=True)
Void set_Id (IsPublic=True)
String get_Name (IsPublic=True)
Void set_Name (IsPublic=True)
Properties:
Int32 Id
String Name
Dynamic Invocation
Invoke methods and set properties dynamically at runtime.
public class Calculator
{
public int Add(int a, int b) => a + b;
public string Greet(string name) => $"Hello, {name}!";
}
Type calcType = typeof(Calculator);
object? calcInstance = Activator.CreateInstance(calcType);
// Invoke Add method
MethodInfo? addMethod = calcType.GetMethod("Add");
object? result = addMethod?.Invoke(calcInstance, new object[] { 3, 4 });
Console.WriteLine($"3 + 4 = {result}");
// Invoke Greet method
MethodInfo? greetMethod = calcType.GetMethod("Greet");
object? greeting = greetMethod?.Invoke(calcInstance, new object[] { "Alice" });
Console.WriteLine(greeting);
Output:
3 + 4 = 7
Hello, Alice!
Working with Attributes
Reflection is the primary way to read custom attributes at runtime.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorAttribute : Attribute
{
public string Name { get; }
public string? Version { get; set; }
public AuthorAttribute(string name) => Name = name;
}
[Author("John Doe", Version = "1.0")]
public class Document
{
[Author("Jane Smith")]
public void Update() { }
}
// Read attributes
Type docType = typeof(Document);
AuthorAttribute? classAttr = docType.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine($"Class author: {classAttr?.Name}, version: {classAttr?.Version}");
MethodInfo? updateMethod = docType.GetMethod("Update");
AuthorAttribute? methodAttr = updateMethod?.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine($"Method author: {methodAttr?.Name}");
Output:
Class author: John Doe, version: 1.0
Method author: Jane Smith
Late Binding with Activator
Create instances of types known only at runtime.
string typeName = "System.Text.StringBuilder";
Type? sbType = Type.GetType(typeName);
if (sbType != null)
{
object? sb = Activator.CreateInstance(sbType);
MethodInfo? append = sbType.GetMethod("Append", new[] { typeof(string) });
MethodInfo? toString = sbType.GetMethod("ToString", Type.EmptyTypes);
append?.Invoke(sb, new object[] { "Built dynamically!" });
string? result = toString?.Invoke(sb, null) as string;
Console.WriteLine(result);
}
Output:
Built dynamically!
Common Mistakes
Performance overhead: Reflection is slower than direct code. Cache
MethodInfo,PropertyInfo, andTypeobjects when calling the same member repeatedly.Ignoring BindingFlags: Forgetting
BindingFlagsparameters causesGetMethodandGetFieldto return null for non-public members.Hardcoding type names: Type names change with Refactoring. Use
nameofor typeof where possible.Not handling missing members: Always check for null after
GetMethodorGetPropertycalls before invoking.Assuming Assembly.GetTypes() is safe: Loading all types can throw
ReflectionTypeLoadExceptionif some types fail to load. UseGetExportedTypesor handle the exception.
Practice Questions
Write a method that takes an object and prints all public properties with their current values.
Create a generic Factory that uses reflection to instantiate a type by its full name from a configuration string.
Implement a method that discovers all classes marked with a custom
[Plugin]attribute in an assembly.Challenge: Build a simple object-to-object mapper (like AutoMapper) that copies properties between two objects of different types using reflection.
FAQ
Mini Project: Object Inspector
Create a utility that prints a detailed report of any object's public members.
using System;
using System.Reflection;
using System.Text;
public static class ObjectInspector
{
public static string Inspect(object obj)
{
Type type = obj.GetType();
var sb = new StringBuilder();
sb.AppendLine($"Type: {type.FullName}");
sb.AppendLine("Properties:");
foreach (PropertyInfo prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
object? value = prop.GetValue(obj);
sb.AppendLine($" {prop.PropertyType.Name} {prop.Name} = {value ?? "null"}");
}
sb.AppendLine("Methods:");
foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
ParameterInfo[] parameters = method.GetParameters();
string paramList = string.Join(", ", parameters.Select(p => $"{p.ParameterType.Name} {p.Name}"));
sb.AppendLine($" {method.ReturnType.Name} {method.Name}({paramList})");
}
return sb.ToString();
}
}
var now = DateTime.Now;
Console.WriteLine(ObjectInspector.Inspect(now));
Output:
Type: System.DateTime
Properties:
Int32 Day = 28
Int32 Month = 6
Int32 Year = 2026
Int32 Hour = ...
Methods:
DateTime AddDays(Double value)
DateTime AddHours(Double value)
...
Reflection is a powerful tool in your C# arsenal. Use it wisely for frameworks, tools, and infrastructure code, but prefer static typing for application logic. The .NET ecosystem depends heavily on reflection for Serialization, DI containers, and testing frameworks.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro