csharp-reflexion

Reflection in C#

  • 5 min

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 also invoke methods or access properties dynamically.

Obtaining Information About Variables and Data Types

One of the main uses 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’s 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 several ways, such as using the typeof operator, the GetType method, or the static Type.GetType method.

int number = 10;
Type typeNumber = number.GetType();
Copied!

In this example, we are getting the data type of the variable number and storing it in the variable typeNumber.

Now we can use the Type to access the rest of the class’s properties and elements. I’ll put 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);
        }
    }
}
Copied!

Modifying Variables and Data Types

Besides obtaining information, we can also use reflection to modify variables and data types at runtime. To access and modify properties, we use PropertyInfo.

For example, suppose 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 typePerson = person.GetType();

PropertyInfo propertyName = typePerson.GetProperty("Name");
propertyName.SetValue(person, "Luis");
Copied!

This would be the Persona class we use in the example

public class Person
{
    public string Name { get; set; }
}
Copied!

In this example,

  • We are getting the Name property of the Person class using reflection
  • Then, we use the SetValue() method to change its value to “Luis”.

Dynamic Instance Creation

Reflection allows creating instances of types dynamically 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));
Copied!

Here,

  • We create an instance of Person at runtime
  • Set its Name property via reflection.

Method Invocation

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);
Copied!
public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }
}
Copied!

In this example, the Add method of the Calculator class is invoked dynamically and the result is printed.

Loading Assemblies at Runtime

Reflection allows loading assemblies at runtime and exploring their types and members.

Assembly assembly = Assembly.Load("mscorlib");
Type[] types = assembly.GetTypes();

foreach (var type in types)
{
	Console.WriteLine("Type: " + type.Name);
}
Copied!

This example loads the mscorlib assembly and lists all the types it contains.

Using Custom Attributes

Reflection allows working with custom attributes, enabling you 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);
}
Copied!
[Info("This is an example class.")]
public class MyClass
{
    [Info("This is an example method.")]
    public void ExampleMethod() { }
}
Copied!
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyAttribute : Attribute
{
    public string Description { get; }

    public MyAttribute(string description)
    {
        Description = description;
    }
}
Copied!

In this example,

  • Custom attributes are defined and used to provide descriptions for a class and a method
  • Those attributes are accessed at runtime using reflection

Reflection is an advanced and very useful feature that allows us to do things that otherwise wouldn’t be possible. But, in general, reflection is slow and complex.

Therefore, it should be used with caution and only when it’s really necessary.