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.
Getting 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.
You can obtain an instance of Type
in various ways, such as using the typeof
operator, the GetType
method, or the static method Type.GetType
.
int number = 10;
Type numberType = number.GetType();
In this example, we are obtaining the data type of the variable number
and storing it in the variable numberType
.
Now we can use the Type
to access the rest of the properties and elements of the class. I will put a complete example to see it well 👇
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, let’s say we have a Person
class with a Name
property and we want to change its value at runtime. We can achieve this using reflection as follows:
Person person = new Person();
Type personType = person.GetType();
PropertyInfo nameProperty = personType.GetProperty("Name");
nameProperty.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 you to create instances of dynamic types at runtime using Activator.CreateInstance
.
Type personType = typeof(Person);
object instance = Activator.CreateInstance(personType);
PropertyInfo nameProperty = personType.GetProperty("Name");
nameProperty.SetValue(instance, "Luis");
Console.WriteLine("Name: " + nameProperty.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 utilize MethodInfo
.
Type calculatorType = typeof(Calculator);
object instance = Activator.CreateInstance(calculatorType);
MethodInfo addMethod = calculatorType.GetMethod("Add");
object result = addMethod.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 you 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 you 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 classAttribute)
{
Console.WriteLine("Class description: " + classAttribute.Description);
}
MethodInfo method = type.GetMethod("ExampleMethod");
if (method.GetCustomAttributes(typeof(MyAttribute), false).FirstOrDefault() is MyAttribute methodAttribute)
{
Console.WriteLine("Method description: " + methodAttribute.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.
- These attributes are accessed at runtime using reflection.
Reflection is an advanced and very useful feature that allows us to do things that would not otherwise be possible. However, in general, reflection is slow and complex.
Therefore, it should be used with caution and only when it is really necessary.