*
Previous Generics in C# Attributes in C# Next

🔍 Reflection in C#

Reflection in C#

Reflection in C# is the ability of a program to inspect and manipulate its own metadata during runtime. It allows you to obtain information about types (classes, interfaces, structs), members (properties, methods, fields), and assemblies. Reflection is a powerful feature that enables dynamic and flexible programming, but it should be used judiciously due to performance overhead.

Core Concepts of C# Reflection

The central class for using reflection is System.Type. You can get an instance of Type in several ways:

From a known type at compile time:

Type type = typeof(string);

Use code with caution.

From an object instance at runtime:

string myString = "hello";
Type type = myString.GetType();

Use code with caution.

From a type’s name as a string:

Type? type = Type.GetType("System.String");

Use code with caution.

Once you have a Type object, you can access all the metadata associated with it.

How to Use Reflection in C#

1. Inspecting a Type and Its Members

This example shows how to get information about a class, including its properties and methods.

using System;
using System.Reflection;

public class MyClass
{
    public int MyProperty { get; set; }
    public void MyMethod(string message)
    {
        Console.WriteLine($"Method called with message: {message}");
    }
}

public class ReflectionExample
{
    public static void Main()
    {
        Type myType = typeof(MyClass);

        Console.WriteLine($"Type Name: {myType.Name}");
        Console.WriteLine($"Full Name: {myType.FullName}");

        Console.WriteLine("\n--- Properties ---");
        foreach (PropertyInfo property in myType.GetProperties())
        {
            Console.WriteLine($"Property Name: {property.Name}");
        }

        Console.WriteLine("\n--- Methods ---");
        foreach (MethodInfo method in myType.GetMethods())
        {
            if (method.DeclaringType == myType)
            {
                Console.WriteLine($"Method Name: {method.Name}");
            }
        }
    }
}

Use code with caution.

Output:

Type Name: MyClass
Full Name: MyClass

--- Properties ---
Property Name: MyProperty

--- Methods ---
Method Name: MyMethod 

2. Creating an Object and Invoking a Method Dynamically

This is known as late binding, where you do not know the type at compile time. The following example shows how to create an instance and call a method using strings.

using System;
using System.Reflection;

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

public class DynamicInvocationExample
{
    public static void Main()
    {
        Type? greeterType = Type.GetType("Greeter");

        if (greeterType != null)
        {
            object? greeterInstance = Activator.CreateInstance(greeterType);
            MethodInfo? sayHelloMethod = greeterType.GetMethod("SayHello");

            if (greeterInstance != null && sayHelloMethod != null)
            {
                sayHelloMethod.Invoke(greeterInstance, new object[] { "Reflection" });
            }
        }
    }
}

Use code with caution.

Output:

Hello, Reflection!

Advantages and Disadvantages

Advantage Disadvantage
Enables extensibility: Build plugin-based architectures by loading assemblies at runtime. Performance overhead: Reflection is slower than direct method calls due to runtime type resolution.
Late binding: Allows calling members not known at compile time. Breaks strong typing: May cause runtime errors if the member doesn’t exist or is incompatible.
Inspects metadata: Useful for reading custom attributes, e.g., in serializers or ORMs. Exposes internals: Can bypass access modifiers, breaking encapsulation.
Facilitates unit testing: Reflection identifies test methods via attributes. Hard to maintain: Code using reflection is harder to read and debug.

When to Use Reflection

Reflection is powerful but should be used only when compile-time mechanisms are insufficient. Common use cases include:

  • Frameworks and Libraries: Frameworks like ASP.NET and Entity Framework use reflection to discover and configure components.
  • Plugins: Dynamically loading and executing external code without compile-time knowledge of types.
  • ORMs: Object-relational mappers use reflection to map database columns to object properties.
  • Runtime Type Discovery: Identifying object types and their members dynamically.
  • Serialization: Inspecting object properties to serialize and deserialize data.

Reflection is a powerful feature in C# that allows you to inspect and interact with metadata about assemblies, types, and members at runtime. It’s part of the System.Reflection namespace and is widely used in dynamic programming, testing, and tooling.

📘 What Can You Do with Reflection?

  • Inspect types, properties, methods, fields, and attributes.
  • Create instances of types dynamically.
  • Invoke methods and access fields/properties at runtime.
  • Load assemblies and explore their contents.
  • Read custom attributes applied to types or members.

🧪 Basic Example

  using System;
  using System.Reflection;

  public class Student {
      public string Name { get; set; }
      public int Age { get; set; }
      public void DisplayInfo() {
          Console.WriteLine($"Name: {Name}, Age: {Age}");
      }
  }

  class Program {
      static void Main() {
          Type type = typeof(Student);

          Console.WriteLine("Properties:");
          foreach (var prop in type.GetProperties()) {
              Console.WriteLine(prop.Name);
          }

          Console.WriteLine("\nMethods:");
          foreach (var method in type.GetMethods()) {
              Console.WriteLine(method.Name);
          }
      }
  }
  

⚙️ Common Reflection APIs

  • Type.GetProperties() – Gets all public properties.
  • Type.GetFields() – Gets all fields.
  • Type.GetMethods() – Gets all methods.
  • Assembly.Load() – Loads an assembly dynamically.
  • Activator.CreateInstance() – Creates an object dynamically.
  • MethodInfo.Invoke() – Invokes a method at runtime.

✅ Best Practices

  • Use reflection sparingly—prefer compile-time access when possible.
  • Cache reflection results for performance-critical scenarios.
  • Use BindingFlags to control visibility (public/private/static).
  • Validate types and members before invoking them.
  • Use reflection for tooling, plugins, and dynamic scenarios—not everyday logic.

📌 When to Use

  • In plugin architectures or dynamic module loading.
  • For serialization/deserialization frameworks.
  • In testing frameworks to discover and run test methods.
  • To build code analysis or documentation tools.

🚫 When Not to Use

  • In performance-critical code—reflection is slower than direct access.
  • For simple type access—use direct method/property calls instead.
  • In security-sensitive code—reflection can expose internal members.

⚠️ Precautions

  • Handle exceptions like MissingMethodException or TargetInvocationException.
  • Be cautious with private member access—it can break encapsulation.
  • Use reflection responsibly to avoid runtime errors and maintainability issues.

🎯 Advantages

  • Flexibility: Enables dynamic behavior and runtime inspection.
  • Tooling: Powers frameworks like NUnit, xUnit, and ASP.NET.
  • Extensibility: Supports plugin and modular architectures.
  • Automation: Useful for code generation and testing tools.

📝 Conclusion

Reflection in C# is a powerful tool for dynamic programming and runtime inspection. While it offers great flexibility, it should be used judiciously to avoid performance and maintainability issues. Mastering reflection unlocks advanced capabilities in tooling, testing, and extensibility.

Back to Index
Previous Generics in C# Attributes in C# Next
*
*