Reflection is the process by which a program can inspect and modify its own structure and behavior at runtime. In C#, reflection is implemented through the System.Reflection
namespace.
With reflection, you can obtain information about assemblies, modules, types, methods, properties, fields, and events, and you can also invoke methods or access properties dynamically.
Obtaining information about variables and data types
One of the main utilities of reflection in C# is the ability to obtain information about the variables and data types used in our program. We can get details such as the variable name, its type, its current value, among others.
To achieve this, we can use the Type
class in C#. This class allows us to access information about the data types used in our code.
An instance of Type
can be obtained in several ways, such as using the typeof
operator, the GetType
method, or the static method Type.GetType
.
int number = 10;
Type typeNumber = number.GetType();
In this example, we are obtaining the data type of the variable number
and storing it in the variable typeNumber
.
Now we can use Type
to access the rest of the properties and elements of the class. I will provide a complete example to see it clearly 👇
using System;
using System.Reflection;
public class Example
{
public int Field;
public void Method() { }
}
public class Program
{
public static void Main()
{
Type type = typeof(Example);
Console.WriteLine("Type name: " + type.Name);
FieldInfo[] fields = type.GetFields();
foreach (var field in fields)
{
Console.WriteLine("Field: " + field.Name);
}
MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
{
Console.WriteLine("Method: " + method.Name);
}
}
}
Modifying variables and data types
In addition to obtaining information, we can also use reflection to modify variables and data types at runtime. To access and modify properties, PropertyInfo
is used.
For example, suppose we have a class Person
with a property Name
and we want to change its value at runtime. We can achieve this using reflection as follows:
Person person = new Person();
Type typePerson = person.GetType();
PropertyInfo propertyName = typePerson.GetProperty("Name");
propertyName.SetValue(person, "Luis");
This would be the person class we used in the example
public class Person
{
public string Name { get; set; }
}
In this example,
- We are obtaining the
Name
property of thePerson
class using reflection - Then, we use the
SetValue()
method to change its value to “Luis”.
Creating dynamic instances
Reflection allows us to create instances of dynamic types at runtime using Activator.CreateInstance
.
Type typePerson = typeof(Person);
object instance = Activator.CreateInstance(typePerson);
PropertyInfo propertyName = typePerson.GetProperty("Name");
propertyName.SetValue(instance, "Luis");
Console.WriteLine("Name: " + propertyName.GetValue(instance));
Here,
- We create an instance of
Person
at runtime - We set its
Name
property using reflection.
Invoking methods
To invoke methods dynamically, we use MethodInfo
.
Type typeCalculator = typeof(Calculator);
object instance = Activator.CreateInstance(typeCalculator);
MethodInfo methodAdd = typeCalculator.GetMethod("Add");
object result = methodAdd.Invoke(instance, new object[] { 3, 5 });
Console.WriteLine("Result of Add: " + result);
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}
In this example, the Add
method of the Calculator
class is invoked dynamically, and the result is printed.
Loading assemblies at runtime
Reflection allows us to load assemblies at runtime and explore their types and members.
Assembly assembly = Assembly.Load("mscorlib");
Type[] types = assembly.GetTypes();
foreach (var type in types)
{
Console.WriteLine("Type: " + type.Name);
}
This example loads the mscorlib
assembly and lists all the types it contains.
Using custom attributes
Reflection allows us to work with custom attributes, being able to read or add metadata to elements at runtime.
Type type = typeof(MyClass);
if (type.GetCustomAttributes(typeof(MyAttribute), false).FirstOrDefault() is MyAttribute attributeClass)
{
Console.WriteLine("Class description: " + attributeClass.Description);
}
MethodInfo method = type.GetMethod("ExampleMethod");
if (method.GetCustomAttributes(typeof(MyAttribute), false).FirstOrDefault() is MyAttribute attributeMethod)
{
Console.WriteLine("Method description: " + attributeMethod.Description);
}
[Info("This is an example class.")]
public class MyClass
{
[Info("This is an example method.")]
public void ExampleMethod() { }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyAttribute : Attribute
{
public string Description { get; }
public MyAttribute(string description)
{
Description = description;
}
}
In this example,
- Custom attributes are defined and used to provide descriptions to a class and a method
- Those attributes are accessed at runtime using reflection
Reflection is a powerful and advanced feature that allows us to do things that would otherwise not be possible. However, in general, reflection is slow and complex.
Therefore, it should be used with caution and only when absolutely necessary.