Reflection in C# is a powerful feature that allows you to inspect and manipulate code at runtime. If that sounds like some fancy programming magic, it’s because it basically is. And you know what they say — with great power, comes great responsibility.
Reflection in C# provides us with the ability to examine types, and their members, and invoke methods dynamically, opening up a world of possibilities for creative programs. In this article, I’ll provide you with 4 simple code examples illustrating how reflection works in C#.
Reflection in C# allows us to examine and manipulate types at runtime. It gives us the ability to retrieve information about classes, interfaces, methods, properties, and more. Understanding how to retrieve types using reflection is important for more complex and creative applications in C#.
To retrieve types using reflection, we can use the Type
class from the System
namespace. The Type
class provides various methods and properties to work with types dynamically. Here’s an example code snippet that demonstrates how to retrieve types using reflection:
using System;
public class Program
{
public static void Main()
{
// Get the type of a class
Type carType = typeof(Car);
Console.WriteLine($"Type Name: {carType.Name}");
// Get all public methods of a class
MethodInfo[] methods = carType.GetMethods();
Console.WriteLine("Methods:");
foreach (MethodInfo method in methods)
{
Console.WriteLine($"- {method.Name}");
}
// Get all properties of a class
PropertyInfo[] properties = carType.GetProperties();
Console.WriteLine("Properties:");
foreach (PropertyInfo property in properties)
{
Console.WriteLine($"- {property.Name}");
}
}
}
public class Car
{
public string Make { get; set; }
public string Model { get; set; }
public void StartEngine()
{
Console.WriteLine("Engine started");
}
public void StopEngine()
{
Console.WriteLine("Engine stopped");
}
}
In the above code, we first use the typeof
operator to get the type of the Car
class. Then, we can access various information about the type. In this example, we retrieve the type’s name, all public methods, and all properties. Running this code will output the following:
Type Name: Car
Methods:
- StartEngine
- StopEngine
Properties:
- Make
- Model
Retrieving types using reflection can be useful in a variety of scenarios. For example, you might want to dynamically load and instantiate classes based on user input or configuration data. Reflection also enables you to inspect the structure and behavior of code at runtime, and this opens up some doors when you need to make decisions based on information that you don’t have at compile time.
See how this works in this video tutorial:
We previously saw how we could access things like public methods and properties that are available on types. As mentioned, this can have many helpful use cases but it doesn’t *quite* feel like the scary stories we might have heard about reflection. Let’s change that up a bit.
While reflection in C# allows us to look up this information, we can also look up non-public information about types. That’s right. All of that effort that someone put in place to use private/protected and hide details from us on the outside? Gone. We can see everything we want! Muahaha! Oh, right — with great power comes great responsibility. No more evil laughter.
Here’s an example code snippet that demonstrates how to access non-public members using reflection:
using System;
using System.Reflection;
public class Person
{
private string _name;
privateint Age { get; set; }
private void SayHello()
{
Console.WriteLine("Hello!");
}
}
public class Program
{
public static void Main()
{
Type personType = typeof(Person);
// Accessing a field using reflection
FieldInfo nameField = personType.GetField(
"_name",
BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine($"Field Name: {nameField.Name}");
// Accessing a private property using reflection
PropertyInfo ageProperty = personType.GetProperty(
"Age"
BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine($"Property Name: {ageProperty.Name}");
// Accessing a private method using reflection
MethodInfo sayHelloMethod = personType.GetMethod(
"SayHello",
BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine($"Method Name: {sayHelloMethod.Name}");
}
}
In the above code, we define a Person
class with a private field name
, a private property Age
, and a private method SayHello
. In the Main
method, we obtain the Type
object for the Person
class using typeof(Person)
. Using reflection, we can access the field, property, and method by specifying their names and using the GetField
, GetProperty
, and GetMethod
methods of the Type
type, respectively, to retrieve the corresponding members.
But the super important part here? The BindingFlags
enum. We can ask, for instance, non-public members. We combine these two categories of members together using flag enums, which you can learn more about here in this video:
Reflection in C# not only allows us to inspect and retrieve information about objects, but it also gives us the power to modify their data and behavior dynamically at runtime. This means that we can, at runtime, get a member by name (and other conditions) and then make modifications — effectively bypassing a lot of the compile-time checks we normally get. And… we can combine that with the non-public access we just saw! Keep that maniacal evil laughter under control…
To illustrate how modifying objects using reflection works, let’s consider a scenario where we have a simple Person
class with properties representing their name and age:
public class Person
{
public string Name { get; set; }
// NOTE: this probably makes no sense to ever
// just have a private property like this here
// but it's just for demonstration
private int Age { get; set; }
public void PrintInfo()
{
Console.WriteLine($"{Name} - {Age} years old");
}
}
Now, let’s say we want to change the age of a Person
object dynamically based on some condition. With reflection, we can achieve this by accessing and modifying the Age
property at runtime.
// Create a new instance of the Person class
Person person = new Person()
{
Name = "Dev Leader",
};
// Get the Type object for the Person class
Type personType = typeof(Person);
// Get the PropertyInfo object for the Age property
PropertyInfo ageProperty = personType.GetProperty(
"Age",
BindingFlags.NonPublic | BindingFlags.Instance);
// Set the value of the Age property to 35
ageProperty.SetValue(person, 35);
// Prints "Dev Leader - 35 years old")
person.PrintInfo();
In this code example, we first create an instance of the Person
class. Then, using reflection, we obtain the Type
object for the Person
class. From the Type
object, we retrieve the PropertyInfo
object for the Age
property, which is private and not accessible to us traditionally from the outside. Finally, we use the SetValue
method of the PropertyInfo
object to modify the Age
property of the person
object to 35. When we ask the instance to print its info, we see the updated value!
While being able to modify objects dynamically can be incredibly powerful, it also comes with some risks and considerations. Modifying objects using reflection can introduce complexity and potential pitfalls (read that as: “will likely break things”), such as breaking encapsulation and violating the intended behavior of the object. It’s important to exercise caution and ensure that the modifications made using reflection align with the design and requirements of the system — this *probably* shouldn’t be your first course of action for many things.
Learn more about private access and modification in this video tutorial:
Reflection in C# allows us to create instances of types dynamically. This means that we can create objects of a certain class without knowing the class name at compile-time. This flexibility can be particularly useful in scenarios where we need to dynamically instantiate objects based on runtime data or configuration.
To create an object dynamically using reflection, we need to follow a few steps:
Type.GetType()
method and passing the fully qualified name of the class as a string.Activator.CreateInstance()
method to create an instance of the class. This method takes the Type object as a parameter and returns a new instance of the class. We can then cast this instance to the desired type and use it as needed.
Let’s take a look at an example:
string className = "MyNamespace.MyClass";
Type classType = Type.GetType(className);
object instance = Activator.CreateInstance(classType);
MyClass myObject = (MyClass)instance;
In the code above, we start by defining the fully qualified name of the class we want to create an instance of (MyNamespace.MyClass
). We then obtain the Type object by calling Type.GetType()
and passing the class name as a string. Next, we use Activator.CreateInstance()
to create a new instance of the class and cast it to the desired type (MyClass
in this case).
Creating objects dynamically using reflection can be useful in various scenarios, including:
Remember to handle exceptions and ensure proper error-checking when working with reflection and dynamically creating objects! It’s easy to forget that reflection opens some funky doors that we really need to be careful with.
Reflection in C# is a powerful tool that every C# developer should get familiar with because even if you don’t need to use it regularly, it’s helpful to understand. Reflection allows us to examine and manipulate objects at runtime, providing us with great flexibility and control over code in some creative situations — like plugin loading! By leveraging reflection, we can dynamically load types, query and invoke methods, access properties, and even create new objects.
Throughout this article, we explored four code examples that showcased how you can leverage reflection in C#. We discussed how to iterate through members, inspect things that aren’t publicly available, retrieve and set property values dynamically, and dynamically create instances of objects. If you found this useful and you’re looking for more learning opportunities, consider subscribing to my free weekly software engineering newsletter and check out my free videos on YouTube!
Also published here.