Skip to main content

C# Reflection Basics

Introduction

Reflection is a powerful feature in C# that allows your programs to examine and interact with their own structure and behavior at runtime. It provides a way to inspect assemblies, types, methods, properties, and other code elements dynamically, without knowing their names at compile time.

Think of reflection as your application holding up a mirror to see itself. This capability enables you to:

  • Discover and use types in assemblies dynamically
  • Create instances of types at runtime
  • Access and modify properties and fields
  • Invoke methods and constructors
  • Generate new code on the fly

While reflection is an advanced topic, understanding its basics will open up new possibilities in your C# programming journey.

The System.Reflection Namespace

The core functionality for reflection in C# comes from the System.Reflection namespace. To use reflection in your programs, you'll need to include:

csharp
using System.Reflection;

This namespace contains classes like Assembly, Type, MethodInfo, PropertyInfo, and more that are essential for reflection operations.

Getting Type Information

The foundation of reflection is the ability to obtain Type information. There are several ways to do this:

Using typeof Operator

The most direct way to get a type is with the typeof operator:

csharp
Type stringType = typeof(string);
Console.WriteLine($"Type: {stringType.Name}");
Console.WriteLine($"Full Name: {stringType.FullName}");
Console.WriteLine($"Is it a class? {stringType.IsClass}");

// Output:
// Type: String
// Full Name: System.String
// Is it a class? True

Using GetType() Method

If you already have an instance of an object, you can use the GetType() method:

csharp
string message = "Hello, Reflection!";
Type messageType = message.GetType();
Console.WriteLine($"Type: {messageType.Name}");
Console.WriteLine($"Namespace: {messageType.Namespace}");

// Output:
// Type: String
// Namespace: System

Type.GetType Method

You can also get a type by its fully qualified string name:

csharp
Type dateTimeType = Type.GetType("System.DateTime");
Console.WriteLine($"Type: {dateTimeType.Name}");
Console.WriteLine($"Is it a struct? {dateTimeType.IsValueType}");

// Output:
// Type: DateTime
// Is it a struct? True

Examining Type Members

Once you have a Type object, you can inspect its members—methods, properties, fields, events, etc.

Getting Methods

csharp
Type stringType = typeof(string);
MethodInfo[] methods = stringType.GetMethods();

Console.WriteLine($"String type has {methods.Length} methods:");
foreach (var method in methods.Take(5)) // Just show first 5 for brevity
{
Console.WriteLine($"- {method.Name}");
}

// Output:
// String type has 73 methods:
// - ToString
// - Equals
// - Equals
// - GetHashCode
// - Clone

Getting Properties

csharp
Type listType = typeof(List<int>);
PropertyInfo[] properties = listType.GetProperties();

Console.WriteLine($"List<int> type has {properties.Length} properties:");
foreach (var property in properties)
{
Console.WriteLine($"- {property.Name} ({property.PropertyType.Name})");
}

// Output:
// List<int> type has 2 properties:
// - Capacity (Int32)
// - Count (Int32)

Getting Constructors

csharp
Type dateTimeType = typeof(DateTime);
ConstructorInfo[] constructors = dateTimeType.GetConstructors();

Console.WriteLine($"DateTime has {constructors.Length} public constructors:");
foreach (var constructor in constructors)
{
Console.WriteLine($"- Constructor with {constructor.GetParameters().Length} parameters");
}

// Output:
// DateTime has 4 public constructors:
// - Constructor with 3 parameters
// - Constructor with 6 parameters
// - Constructor with 7 parameters
// - Constructor with 8 parameters

Creating Objects with Reflection

Reflection allows you to create objects dynamically:

csharp
// Get the type
Type listType = typeof(List<string>);

// Create an instance using Activator
List<string> myList = (List<string>)Activator.CreateInstance(listType);

// Use the object
myList.Add("Created via reflection");
Console.WriteLine($"List count: {myList.Count}");
Console.WriteLine($"List content: {myList[0]}");

// Output:
// List count: 1
// List content: Created via reflection

For types with constructor parameters:

csharp
// Get the DateTime type
Type dateTimeType = typeof(DateTime);

// Get a specific constructor (year, month, day)
ConstructorInfo constructor = dateTimeType.GetConstructor(new[] {
typeof(int), typeof(int), typeof(int)
});

// Create a DateTime for January 1, 2023
object date = constructor.Invoke(new object[] { 2023, 1, 1 });
Console.WriteLine($"Created date: {date}");

// Output:
// Created date: 1/1/2023 12:00:00 AM

Invoking Methods with Reflection

Reflection lets you call methods dynamically:

csharp
// Create a string
string message = "hello, reflection!";

// Get the type
Type stringType = typeof(string);

// Get the ToUpper method (no parameters)
MethodInfo toUpperMethod = stringType.GetMethod("ToUpper", new Type[0]);

// Invoke the method
string result = (string)toUpperMethod.Invoke(message, null);

Console.WriteLine($"Original: {message}");
Console.WriteLine($"After ToUpper(): {result}");

// Output:
// Original: hello, reflection!
// After ToUpper(): HELLO, REFLECTION!

Accessing and Modifying Properties

You can read and write properties through reflection:

csharp
// Create a List<int>
List<int> numbers = new List<int> { 1, 2, 3 };

// Get the type
Type listType = numbers.GetType();

// Get the Count property
PropertyInfo countProperty = listType.GetProperty("Count");

// Read the property value
int count = (int)countProperty.GetValue(numbers);
Console.WriteLine($"List count: {count}");

// Get the Capacity property
PropertyInfo capacityProperty = listType.GetProperty("Capacity");

// Read the current capacity
int currentCapacity = (int)capacityProperty.GetValue(numbers);
Console.WriteLine($"Current capacity: {currentCapacity}");

// Set the capacity to a new value
capacityProperty.SetValue(numbers, 10);
Console.WriteLine($"New capacity: {numbers.Capacity}");

// Output:
// List count: 3
// Current capacity: 4
// New capacity: 10

Practical Example: Creating a Simple Object Inspector

Let's create a practical example that uses reflection to inspect an object's properties:

csharp
public static void InspectObject(object obj)
{
if (obj == null)
{
Console.WriteLine("Object is null");
return;
}

Type type = obj.GetType();
Console.WriteLine($"Object Type: {type.Name}");
Console.WriteLine("Properties:");

PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
try
{
object value = property.GetValue(obj);
string valueStr = value?.ToString() ?? "null";
Console.WriteLine($"- {property.Name}: {valueStr}");
}
catch (Exception ex)
{
Console.WriteLine($"- {property.Name}: [Error: {ex.Message}]");
}
}
}

// Example usage:
Person person = new Person
{
Name = "John Doe",
Age = 30,
Email = "[email protected]"
};

InspectObject(person);

// Output (assuming Person class with those properties):
// Object Type: Person
// Properties:
// - Name: John Doe
// - Age: 30
// - Email: [email protected]

This simple inspector can be used for debugging or diagnostic purposes, showing you the contents of any object at runtime.

Working with Attributes via Reflection

Reflection allows you to inspect custom attributes applied to types and members:

csharp
// Define a custom attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class VersionAttribute : Attribute
{
public string Version { get; }

public VersionAttribute(string version)
{
Version = version;
}
}

// Apply the attribute to a class
[Version("1.0.0")]
public class SampleService
{
[Version("1.0.1")]
public void DoSomething() { }
}

// Inspect the attributes
Type serviceType = typeof(SampleService);
VersionAttribute classAttr = (VersionAttribute)serviceType.GetCustomAttribute(typeof(VersionAttribute));

Console.WriteLine($"Class version: {classAttr.Version}");

MethodInfo method = serviceType.GetMethod("DoSomething");
VersionAttribute methodAttr = (VersionAttribute)method.GetCustomAttribute(typeof(VersionAttribute));

Console.WriteLine($"Method version: {methodAttr.Version}");

// Output:
// Class version: 1.0.0
// Method version: 1.0.1

Performance Considerations

While reflection is powerful, it comes with performance costs:

  1. Slower execution: Reflection operations are significantly slower than direct code
  2. Memory usage: Reflection can consume more memory due to metadata loading
  3. Type safety: Reflection bypasses compile-time type checking, which can lead to runtime errors

For performance-critical code, consider:

  • Caching reflection results when possible
  • Using alternatives like compiled expressions for frequently executed reflection code
  • Using reflection only when necessary

Summary

Reflection is a powerful feature that allows C# programs to examine and modify their structure and behavior at runtime. In this tutorial, we've covered:

  • Getting type information using typeof, GetType(), and Type.GetType()
  • Examining type members such as methods, properties, and constructors
  • Creating objects dynamically with reflection
  • Invoking methods and accessing properties through reflection
  • Working with attributes
  • Performance considerations when using reflection

Reflection opens up many possibilities for building flexible, dynamic applications, but it should be used judiciously due to its performance impact and reduced type safety.

Additional Resources

Exercises

  1. Write a program that uses reflection to list all public methods of the DateTime struct.
  2. Create a simple serializer that converts an object's properties to a dictionary using reflection.
  3. Write a method that can copy all property values from one object to another of a different type, where property names match.
  4. Build a simple dependency injection container using reflection to create and resolve instances.
  5. Create a custom attribute and write code that uses reflection to find all classes in your assembly with that attribute.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)