C#
reflection
programming
software development
coding tips

what is reflection in C, what are the benefit. How to use it to get benifit

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Reflection in C# lets code inspect metadata and interact with types at runtime. It is useful when type information is not fully known at compile time, such as plugin systems, serializers, and dependency injection containers. Used carefully, reflection improves extensibility, but it should be applied with clear boundaries because runtime inspection has performance and safety tradeoffs.

Core Sections

What reflection provides

The System.Reflection namespace exposes assemblies, types, methods, properties, fields, and custom attributes. You can inspect members, instantiate objects dynamically, and invoke methods by name.

csharp
1using System;
2using System.Reflection;
3
4public class Demo
5{
6    public string Name { get; set; } = "Sample";
7    public void Print() => Console.WriteLine($"Name: {Name}");
8}
9
10public static class Program
11{
12    public static void Main()
13    {
14        Type t = typeof(Demo);
15        Console.WriteLine(t.FullName);
16
17        foreach (PropertyInfo p in t.GetProperties())
18        {
19            Console.WriteLine($"Property: {p.Name} ({p.PropertyType.Name})");
20        }
21    }
22}

Dynamic creation and invocation

Reflection can create instances and call members at runtime. This is useful in plugin loaders and factory patterns.

csharp
1using System;
2using System.Reflection;
3
4public class Worker
5{
6    public void Run(string arg) => Console.WriteLine($"Running with {arg}");
7}
8
9public static class Program
10{
11    public static void Main()
12    {
13        Type type = typeof(Worker);
14        object? instance = Activator.CreateInstance(type);
15
16        MethodInfo? method = type.GetMethod("Run");
17        method?.Invoke(instance, new object[] { "job-42" });
18    }
19}

When invoking by string name, always handle missing members safely to avoid runtime exceptions.

Practical benefit with attribute scanning

A common production use case is discovering classes marked with custom attributes.

csharp
1using System;
2using System.Linq;
3using System.Reflection;
4
5[AttributeUsage(AttributeTargets.Class)]
6public class HandlerAttribute : Attribute
7{
8    public string Key { get; }
9    public HandlerAttribute(string key) => Key = key;
10}
11
12[Handler("invoice")]
13public class InvoiceHandler { }
14
15public static class Program
16{
17    public static void Main()
18    {
19        var handlers = Assembly.GetExecutingAssembly()
20            .GetTypes()
21            .Where(t => t.GetCustomAttribute<HandlerAttribute>() != null)
22            .ToList();
23
24        foreach (var h in handlers)
25        {
26            Console.WriteLine($"Discovered: {h.Name}");
27        }
28    }
29}

This pattern powers plugin registration and convention based startup configuration.

Performance and maintainability guidance

Reflection is slower than direct compiled calls because metadata lookup and dynamic dispatch happen at runtime. In hot paths, cache MethodInfo and compiled delegates.

csharp
1using System;
2using System.Linq.Expressions;
3using System.Reflection;
4
5public class MathOps { public int Double(int x) => x * 2; }
6
7public static class Program
8{
9    public static void Main()
10    {
11        var instance = new MathOps();
12        MethodInfo method = typeof(MathOps).GetMethod(nameof(MathOps.Double))!;
13
14        var target = Expression.Parameter(typeof(MathOps), "t");
15        var arg = Expression.Parameter(typeof(int), "x");
16        var call = Expression.Call(target, method, arg);
17        var fn = Expression.Lambda<Func<MathOps, int, int>>(call, target, arg).Compile();
18
19        Console.WriteLine(fn(instance, 21));
20    }
21}

Caching and compiled delegates reduce repeated reflection overhead significantly.

Security and boundary concerns

Reflection can access internal details if permissions and binding flags allow it. Keep usage constrained, validate discovered types, and avoid exposing unrestricted reflection hooks in untrusted environments.

Common Pitfalls

  • Using reflection in tight loops without caching metadata. Cache type and member lookups to reduce overhead.
  • Invoking members by hard coded strings without validation. Guard missing methods and signature mismatches.
  • Treating reflection as default architecture for all code paths. Use it where dynamic behavior is truly needed.
  • Scanning entire assemblies repeatedly at runtime startup and request paths. Precompute discovery results during initialization.
  • Ignoring security implications when loading unknown assemblies or types. Validate trusted sources and apply allowlists.

Summary

  • Reflection enables runtime type inspection and dynamic invocation in C#.
  • It is valuable for plugins, serializers, and attribute driven registration.
  • Runtime flexibility comes with performance and safety tradeoffs.
  • Cache reflection artifacts and isolate dynamic logic behind clear abstractions.
  • Use reflection deliberately, not as a substitute for strong static design.

Additional implementation notes: keep boundaries explicit, validate assumptions with tests, and prefer maintainable conventions over clever shortcuts when designing shared code paths.


Course illustration
Course illustration

All Rights Reserved.