C# – Reflection

By | 20/11/2024

In this post, we will see what Reflection is and how use it in our programs.
But first of all, what is Reflection?
“Reflection is part of the System.Reflection namespace in C# that enables us to analyse assemblies and types, create instances dynamically, access properties and fields, invoke methods, and even discover custom attributes. Through reflection, we can work with code in a highly flexible manner, which is essential when building frameworks or libraries that interact with unknown types”.
In a nutshell with Reflection we can obtain information about assemblies, modules, and types, including classes, interfaces, and value types.

Let’s see some practical examples to see how to use Reflection effectively.

RETRIEVING TYPE INFORMATION:
We can use Reflection to interact with a class to get the Type object, the list its methods, properties, and fields, regardless of their access modifiers.

[PERSON.CS]

namespace TestReflection;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void Introduce()
    {
        Console.WriteLine($"Hi, I'm {Name}, and I'm {Age} years old.");
    }
}

[PROGRAM.CS]

// See https://aka.ms/new-console-template for more information

using System.Reflection;
using TestReflection;

// Get the Type object for the class 'Person'.
Type personType = typeof(Person);

// Dynamically we create an instance of the 'Person' class.
object personInstance = Activator.CreateInstance(personType);

// Get the list of Properties
var lstProperties = personType.GetProperties();
Console.WriteLine("Properties list");
foreach (var prop in lstProperties)
{
    Console.WriteLine(prop.Name);
}
Console.WriteLine();

// Get the PropertyInfo object for the 'Name' property of the 'Person' class.
PropertyInfo nameProperty = personType.GetProperty("Name");

// Set the value of the 'Name' property on the dynamically created 'Person' instance.
nameProperty.SetValue(personInstance, "Alice");

// Get the PropertyInfo object for the 'Age' property of the 'Person' class.
PropertyInfo ageProperty = personType.GetProperty("Age");

// Set the value of the 'Age' property on the 'Person' instance to 30.
ageProperty.SetValue(personInstance, 30);

// Get the list of Properties
var lstMethods = personType.GetMethods();
Console.WriteLine("Methods list");
foreach (var met in lstMethods)
{
    Console.WriteLine(met.Name);
}
Console.WriteLine();

// Get the MethodInfo object for the 'Introduce' method of the 'Person' class.
MethodInfo introduceMethod = personType.GetMethod("Introduce");

// Invoke the 'Introduce' method on the 'Person' instance.
introduceMethod.Invoke(personInstance, null);


EXAMINING FIELDS AND PROPERTIES:
We can use Reflection to get or set the values of fields and properties, even if they are private.

[PERSON.CS]

namespace TestReflection;

public class Person
{
    private string name;
    public int Age { get; set; }

    public Person(string name, int age)
    {
        this.name = name;
        Age = age;
    }
    
    public void Introduce()
    {
        Console.WriteLine($"Hi, I'm {this.name}, and I'm {Age} years old.");
    } 
}

[PROGRAM.CS]

using System.Reflection; 
using TestReflection;    

// Create a new instance of 'Person' with the name "Alice" and age 30
Person person = new Person("Alice", 30);

// Call the 'Introduce' method, which likely outputs the person's details
person.Introduce();

// Get the Type object corresponding to the 'Person' class
Type type = typeof(Person);

// Obtain the FieldInfo for the private field 'name' in the 'Person' class
FieldInfo fieldName = type.GetField("name", BindingFlags.NonPublic | BindingFlags.Instance);

// Retrieve and display the current value of the private field 'name' from the 'person' instance
Console.WriteLine("Name (before): " + fieldName.GetValue(person));

// Modify the private field 'name' of the 'person' instance to "Damiano"
fieldName.SetValue(person, "Damiano");

// Retrieve and display the new value of the private field 'name' to confirm the change
Console.WriteLine("Name (after): " + fieldName.GetValue(person));

// Obtain the PropertyInfo for the public property 'Age' in the 'Person' class
PropertyInfo propertyAge = type.GetProperty("Age");

// Retrieve and display the current value of the 'Age' property from the 'person' instance
Console.WriteLine("Age (before): " + propertyAge.GetValue(person));

// Modify the 'Age' property of the 'person' instance to 35
propertyAge.SetValue(person, 35);

// Retrieve and display the new value of the 'Age' property to confirm the change
Console.WriteLine("Age (after): " + propertyAge.GetValue(person));

// Call the 'Introduce' method again to reflect the updated 'name' and 'Age' values
person.Introduce();


INVOKING GENERIC METHODS:
We can use Reflection to invoke generic methods. This is especially useful when the type parameters are not known until runtime, allowing for more generic and flexible code.

[PERSON.CS]

namespace TestReflection;

public class Person
{
    public void Print<T>(T parameter)
    {
        Console.WriteLine($"Parameter Type: {typeof(T)}, Value: {parameter}");
    }
}

[PROGRAM.CS]

using System.Reflection;  
using TestReflection;     

// Create an instance of the 'Person' class
Person instancePerson = new Person();

// Obtain the MethodInfo object for the generic method 'Print' in the 'Person' class
MethodInfo method = typeof(Person).GetMethod("Print");

// Create a generic method by specifying the type argument 'int' for the generic method 'Print<T>'
MethodInfo genericMethod = method.MakeGenericMethod(typeof(int));

// Invoke the constructed generic method 'Print<int>' on 'instancePerson' with the argument 42
genericMethod.Invoke(instancePerson, new object[] { 42 });

// Create another generic method by specifying the type argument 'string' for 'Print<T>'
genericMethod = method.MakeGenericMethod(typeof(string));

// Invoke the constructed generic method 'Print<string>' on 'instancePerson' with the argument "Hello, World!"
genericMethod.Invoke(instancePerson, new object[] { "Hello, World!" });


TESTING PRIVATE METHODS:
We can use Reflection to test a private method.
Here, the link to the post where we saw this case.



Reflection allows us to create flexible and dynamic applications that can adapt to various types and data structures at runtime. However, it is essential to consider the performance and maintenance implications before using Reflection, as it can add complexity and slow down the application. Reflection involves runtime type inspection and metadata manipulation, which can introduce overhead that could slow down the application, especially in performance-critical areas. So, when deciding to use Reflection effectively, we should balance its benefits with its performance impact.




Category: C# Tags:

Leave a Reply

Your email address will not be published. Required fields are marked *