C# -

Reflection

Reflection in C# is a powerful feature that allows you to inspect and interact with metadata about assemblies, types, and members at runtime. This tutorial will cover the basics, techniques, best practices, and advanced concepts of reflection.


1. Introduction to Reflection

Reflection is the ability to inspect the structure of your code at runtime. It allows you to access information about loaded assemblies, modules, and types, and to create and manipulate objects dynamically.


2. Getting Type Information

You can obtain type information using the `Type` class. Here’s how to get type information for a given object:

        
            using System;

namespace ReflectionExamples;

public class Program
{
    public static void Main(string[] args)
    {
        Type type = typeof(string);
        Console.WriteLine($"Type: {type.FullName}");
        Console.WriteLine($"Namespace: {type.Namespace}");
        Console.WriteLine($"Assembly: {type.Assembly.FullName}");
    }
}
        
    

3. Inspecting Members

Reflection allows you to inspect members of a type, including fields, properties, methods, and events. Here’s an example:

        
            using System;
using System.Reflection;

namespace ReflectionExamples;

public class Program
{
    public static void Main(string[] args)
    {
        Type type = typeof(SampleClass);
        Console.WriteLine($"Type: {type.FullName}");

        foreach (var member in type.GetMembers())
        {
            Console.WriteLine($"{member.MemberType}: {member.Name}");
        }
    }
}

public class SampleClass
{
    public int Field;
    public int Property { get; set; }
    public void Method() { }
    public event EventHandler Event;
}
        
    

4. Invoking Methods

You can dynamically invoke methods using reflection. Here’s how to invoke a method at runtime:

        
            using System;
using System.Reflection;

namespace ReflectionExamples;

public class Program
{
    public static void Main(string[] args)
    {
        Type type = typeof(SampleClass);
        object instance = Activator.CreateInstance(type);

        MethodInfo method = type.GetMethod("SayHello");
        method.Invoke(instance, new object[] { "World" });
    }
}

public class SampleClass
{
    public void SayHello(string name)
    {
        Console.WriteLine($"Hello, {name}!");
    }
}
        
    

5. Accessing Fields and Properties

Reflection allows you to access and modify fields and properties. Here’s an example of how to do that:

        
            using System;
using System.Reflection;

namespace ReflectionExamples;

public class Program
{
    public static void Main(string[] args)
    {
        Type type = typeof(SampleClass);
        object instance = Activator.CreateInstance(type);

        FieldInfo field = type.GetField("Field");
        field.SetValue(instance, 42);
        Console.WriteLine($"Field: {field.GetValue(instance)}");

        PropertyInfo property = type.GetProperty("Property");
        property.SetValue(instance, 84);
        Console.WriteLine($"Property: {property.GetValue(instance)}");
    }
}

public class SampleClass
{
    public int Field;
    public int Property { get; set; }
}
        
    

6. Creating Instances

You can create instances of types dynamically using reflection. Here’s how to instantiate a type at runtime:

        
            using System;

namespace ReflectionExamples;

public class Program
{
    public static void Main(string[] args)
    {
        Type type = typeof(SampleClass);
        object instance = Activator.CreateInstance(type);

        Console.WriteLine($"Instance created: {instance}");
    }
}

public class SampleClass
{
    public SampleClass()
    {
        Console.WriteLine("SampleClass constructor called.");
    }
}
        
    

7. Working with Attributes

Attributes provide a powerful way to add metadata to your code. You can use reflection to read attributes at runtime. Here’s an example:

        
            using System;
using System.Reflection;

namespace ReflectionExamples;

public class Program
{
    public static void Main(string[] args)
    {
        Type type = typeof(SampleClass);
        foreach (var attribute in type.GetCustomAttributes())
        {
            Console.WriteLine($"Attribute: {attribute}");
        }
    }
}

[Sample]
public class SampleClass
{
}

public class SampleAttribute : Attribute
{
}
        
    

8. Reflection and Generics

Reflection can also be used with generics, allowing you to inspect and manipulate generic types. Here’s how to work with generics in reflection:

        
            using System;
using System.Reflection;

namespace ReflectionExamples;

public class Program
{
    public static void Main(string[] args)
    {
        Type genericType = typeof(GenericClass<>);
        Type constructedType = genericType.MakeGenericType(typeof(int));
        object instance = Activator.CreateInstance(constructedType);

        MethodInfo method = constructedType.GetMethod("GenericMethod");
        method.Invoke(instance, new object[] { 123 });
    }
}

public class GenericClass<T>
{
    public void GenericMethod(T value)
    {
        Console.WriteLine($"Value: {value}");
    }
}
        
    

9. Loading Assemblies

You can load assemblies at runtime using reflection. Here’s an example of how to load an assembly and inspect its contents:

        
            using System;
using System.Reflection;

namespace ReflectionExamples;

public class Program
{
    public static void Main(string[] args)
    {
        Assembly assembly = Assembly.Load("mscorlib");
        Console.WriteLine($"Assembly: {assembly.FullName}");

        foreach (var type in assembly.GetTypes())
        {
            Console.WriteLine($"Type: {type.FullName}");
        }
    }
}
        
    

10. Emitting Dynamic Methods

Reflection.Emit allows you to create and execute dynamic methods at runtime. Here’s how to emit a dynamic method:

        
            using System;
using System.Reflection.Emit;

namespace ReflectionExamples;

public class Program
{
    public static void Main(string[] args)
    {
        DynamicMethod dynamicMethod = new DynamicMethod("DynamicHello", null, new Type[] { typeof(string) });

        ILGenerator il = dynamicMethod.GetILGenerator();
        il.Emit(OpCodes.Ldstr, "Hello, ");
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Call, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }));
        il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", Type.EmptyTypes));
        il.Emit(OpCodes.Ret);

        var hello = (Action<string>)dynamicMethod.CreateDelegate(typeof(Action<string>));
        hello("Dynamic World");
    }
}
        
    


11. Performance Considerations

Using reflection can have performance implications. This section covers best practices to minimize performance overhead when using reflection:

        
            using System;
using System.Diagnostics;
using System.Reflection;

namespace ReflectionExamples;

public class Program
{
    public static void Main(string[] args)
    {
        Type type = typeof(SampleClass);
        MethodInfo method = type.GetMethod("TestMethod");
        object instance = Activator.CreateInstance(type);

        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < 1000000; i++)
        {
            method.Invoke(instance, null);
        }
        sw.Stop();

        Console.WriteLine($"Reflection: {sw.ElapsedMilliseconds} ms");

        sw.Restart();
        for (int i = 0; i < 1000000; i++)
        {
            ((SampleClass)instance).TestMethod();
        }
        sw.Stop();

        Console.WriteLine($"Direct: {sw.ElapsedMilliseconds} ms");
    }
}

public class SampleClass
{
    public void TestMethod() { }
}
        
    

12. Security Considerations

Reflection can bypass normal access controls, so it’s important to understand the security implications. Here’s a discussion on reflection and security:

        
            using System;
using System.Reflection;

namespace ReflectionExamples;

public class Program
{
    public static void Main(string[] args)
    {
        Type type = typeof(SampleClass);
        MethodInfo privateMethod = type.GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        object instance = Activator.CreateInstance(type);

        privateMethod.Invoke(instance, null);
    }
}

public class SampleClass
{
    private void PrivateMethod()
    {
        Console.WriteLine("Private method called.");
    }
}
        
    

13. Use Cases for Reflection

Reflection can be used in a variety of scenarios, such as testing frameworks, ORMs, and more. Here are some common use cases:

        
            using System;

namespace ReflectionExamples;

public class Program
{
    public static void Main(string[] args)
    {
        // Use case 1: Testing framework
        Console.WriteLine("Running tests...");
        TestRunner.RunTests();

        // Use case 2: ORM
        Console.WriteLine("Mapping objects...");
        ORM.MapObject(new SampleClass { Id = 1, Name = "Test" });

        // Use case 3: Dependency Injection
        Console.WriteLine("Resolving dependencies...");
        var service = ServiceResolver.Resolve<IService>();
        service.DoWork();
    }
}

public class SampleClass
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public static class TestRunner
{
    public static void RunTests()
    {
        Console.WriteLine("Tests completed.");
    }
}

public static class ORM
{
    public static void MapObject(object obj)
    {
        Console.WriteLine("Object mapped.");
    }
}

public interface IService
{
    void DoWork();
}

public class Service : IService
{
    public void DoWork()
    {
        Console.WriteLine("Work done.");
    }
}

public static class ServiceResolver
{
    public static T Resolve<T>()
    {
        return (T)Activator.CreateInstance(typeof(Service));
    }
}
        
    

14. Conclusion

Reflection is a versatile and powerful feature in C#. By understanding and leveraging reflection, you can create dynamic and flexible applications. Always consider the performance and security implications when using reflection in your projects.