Reflection in C# is a powerful feature that allows you to inspect and interact with the metadata of types at runtime. This includes examining the properties, methods, and events of an object, as well as creating instances of types dynamically. Reflection is particularly useful for scenarios such as building frameworks, performing dynamic type discovery, and creating custom attributes.

Key Concepts

  1. Metadata: Information about the types defined in a program, such as classes, interfaces, and methods.
  2. System.Reflection Namespace: Contains classes that allow you to obtain information about assemblies, modules, members, parameters, and other entities in managed code.
  3. Type Class: Represents type declarations: class types, interface types, array types, value types, and enumeration types.

Practical Examples

Example 1: Inspecting a Type

Let's start by inspecting a simple class using reflection.

using System;
using System.Reflection;

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

    public void SayHello()
    {
        Console.WriteLine("Hello!");
    }
}

class Program
{
    static void Main()
    {
        Type personType = typeof(Person);

        Console.WriteLine("Properties of Person:");
        foreach (PropertyInfo prop in personType.GetProperties())
        {
            Console.WriteLine($"Property: {prop.Name}, Type: {prop.PropertyType}");
        }

        Console.WriteLine("\nMethods of Person:");
        foreach (MethodInfo method in personType.GetMethods())
        {
            Console.WriteLine($"Method: {method.Name}, Return Type: {method.ReturnType}");
        }
    }
}

Explanation:

  • typeof(Person): Gets the Type object representing the Person class.
  • GetProperties(): Retrieves an array of PropertyInfo objects representing all the public properties of the Person class.
  • GetMethods(): Retrieves an array of MethodInfo objects representing all the public methods of the Person class.

Example 2: Creating an Instance Dynamically

You can also create an instance of a type dynamically using reflection.

using System;
using System.Reflection;

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

    public void SayHello()
    {
        Console.WriteLine("Hello!");
    }
}

class Program
{
    static void Main()
    {
        Type personType = typeof(Person);
        object personInstance = Activator.CreateInstance(personType);

        PropertyInfo nameProperty = personType.GetProperty("Name");
        nameProperty.SetValue(personInstance, "John Doe");

        PropertyInfo ageProperty = personType.GetProperty("Age");
        ageProperty.SetValue(personInstance, 30);

        MethodInfo sayHelloMethod = personType.GetMethod("SayHello");
        sayHelloMethod.Invoke(personInstance, null);

        Console.WriteLine($"Name: {nameProperty.GetValue(personInstance)}, Age: {ageProperty.GetValue(personInstance)}");
    }
}

Explanation:

  • Activator.CreateInstance(personType): Creates an instance of the Person class.
  • GetProperty("Name"): Retrieves the PropertyInfo object for the Name property.
  • SetValue(personInstance, "John Doe"): Sets the value of the Name property to "John Doe".
  • GetMethod("SayHello"): Retrieves the MethodInfo object for the SayHello method.
  • Invoke(personInstance, null): Invokes the SayHello method on the personInstance.

Practical Exercises

Exercise 1: Inspecting a Custom Class

Create a class named Car with properties Make, Model, and Year. Use reflection to list all the properties and methods of the Car class.

Solution:

using System;
using System.Reflection;

public class Car
{
    public string Make { get; set; }
    public string Model { get; set; }
    public int Year { get; set; }

    public void Drive()
    {
        Console.WriteLine("Driving...");
    }
}

class Program
{
    static void Main()
    {
        Type carType = typeof(Car);

        Console.WriteLine("Properties of Car:");
        foreach (PropertyInfo prop in carType.GetProperties())
        {
            Console.WriteLine($"Property: {prop.Name}, Type: {prop.PropertyType}");
        }

        Console.WriteLine("\nMethods of Car:");
        foreach (MethodInfo method in carType.GetMethods())
        {
            Console.WriteLine($"Method: {method.Name}, Return Type: {method.ReturnType}");
        }
    }
}

Exercise 2: Dynamic Method Invocation

Create an instance of the Car class dynamically and set its properties using reflection. Then, invoke the Drive method.

Solution:

using System;
using System.Reflection;

public class Car
{
    public string Make { get; set; }
    public string Model { get; set; }
    public int Year { get; set; }

    public void Drive()
    {
        Console.WriteLine("Driving...");
    }
}

class Program
{
    static void Main()
    {
        Type carType = typeof(Car);
        object carInstance = Activator.CreateInstance(carType);

        PropertyInfo makeProperty = carType.GetProperty("Make");
        makeProperty.SetValue(carInstance, "Toyota");

        PropertyInfo modelProperty = carType.GetProperty("Model");
        modelProperty.SetValue(carInstance, "Corolla");

        PropertyInfo yearProperty = carType.GetProperty("Year");
        yearProperty.SetValue(carInstance, 2021);

        MethodInfo driveMethod = carType.GetMethod("Drive");
        driveMethod.Invoke(carInstance, null);

        Console.WriteLine($"Make: {makeProperty.GetValue(carInstance)}, Model: {modelProperty.GetValue(carInstance)}, Year: {yearProperty.GetValue(carInstance)}");
    }
}

Common Mistakes and Tips

  • Access Modifiers: Reflection can only access public members by default. To access non-public members, you need to use binding flags.
  • Performance: Reflection is slower than direct code access. Use it judiciously, especially in performance-critical applications.
  • Security: Be cautious when using reflection to invoke methods or access properties, as it can potentially expose private data or methods.

Conclusion

Reflection is a versatile tool in C# that allows you to inspect and manipulate types at runtime. It is particularly useful for dynamic type discovery, building frameworks, and creating custom attributes. However, it should be used judiciously due to its performance overhead and potential security implications. In the next topic, we will explore attributes, which are closely related to reflection and provide a way to add metadata to your code.

© Copyright 2024. All rights reserved