5 Practical Applications of .NET Reflection

published on 28 October 2024

Need to peek inside your code while it's running? That's what .NET Reflection does. Here are 5 real ways developers use it:

Application What It Does When To Use
Plugin Systems Loads new code without rebuilding Adding features on-demand
Data Validation Checks data using attributes Form validation, input checking
Object Mapping Copies data between objects DTOs, API responses
Test Frameworks Finds and runs tests Unit testing, mocking
App Settings Manages configuration Reading/writing settings

Important: Reflection trades speed for flexibility:

  • It runs 30x slower than direct code
  • Cached reflection is 33x faster than uncached
  • Pre-compiled expressions are best for speed

Quick Tips:

  • Cache reflection calls you'll use again
  • Add security checks for private code
  • Handle errors that pop up at runtime
  • Test performance with real data
  • Use regular code when types are known

Want the full details? Let's break down each use case with real code examples and performance numbers.

How .NET Reflection Works

.NET Reflection is like X-ray vision for your code - it lets your program look at itself while running. Here's a simple example:

// Basic reflection example
String name = "Test";
Type type = name.GetType();
Console.WriteLine(type); // Outputs: System.String

The core pieces work together:

Component What It Does How To Use It
Assembly Holds your code Assembly.Load("MyLibrary.dll")
Type Maps your classes typeof(DateTime)
MethodInfo Shows methods type.GetMethod("ToString")
PropertyInfo Gets properties type.GetProperty("Length")
FieldInfo Accesses fields type.GetField("_count")

Let's break down how to use it:

1. Load Your Code

Assembly testAssembly = Assembly.LoadFile(@"c:\Test.dll");
Type calcType = testAssembly.GetType("Test.Calculator");

2. Check Type Details

Type t = typeof(Enumerable);
Console.WriteLine($"Name: {t.Name}");
Console.WriteLine($"Namespace: {t.Namespace}");

3. Make New Objects

DateTime date = (DateTime)Activator.CreateInstance(typeof(DateTime));

Performance is key. Here's what you need to know:

Operation Speed What To Do
Property access Slow Save property info
Object creation Medium Use compiled code
Reading metadata Fast Nothing needed

Make it faster by:

  • Saving type info you'll use again
  • Using BindingFlags to search less
  • Testing your specific use case

Know these boundaries:

What Limit Impact
Security Can see private code Need right permissions
Speed Not as fast as direct code Cache when you can
Errors Found while running Add extra checks

The CLR manages assemblies in domains, keeping everything in check. This setup helps reflection do its job while keeping your code safe and controlled.

Building Plugin Systems

Want to add features to your app without touching the core code? That's what plugin systems do. Here's how to build one using reflection:

Let's start with a simple plugin contract:

namespace PluginBase {
    public interface ICommand {
        string Name { get; }
        string Description { get; }
        int Execute();
    }
}

A plugin system needs these key parts:

Part What It Does How It Works
Contract Sets the rules Interface/abstract class that plugins must follow
Implementation Does the work Class that brings the contract to life
Loader Finds plugins Uses reflection to grab DLLs

Here's a plugin loader that gets the job done:

foreach (Type t in assembly.GetTypes()) {    
    if (!t.IsInterface) {        
        var plugin = t.GetInterface(typeof(ICommand).Name);        
        if (plugin != null) {                
            ICommand p = (ICommand)Activator.CreateInstance(t);                
            plugins.Add(p);            
        }    
    }
}

Need to handle plugin dependencies? Here's what to do:

What How Why
Load assemblies AssemblyLoadContext Keeps plugins separate
Find dependencies AssemblyDependencyResolver Maps DLL locations
Check interfaces Use full name checks Prevents naming conflicts

Make your plugin system better:

  • Store plugins in their own folder
  • Check plugin signatures
  • Save loaded plugin types
  • Test each plugin by itself

The result? Your app stays clean while plugins can change whenever needed.

2. Data Validation with Attributes

Data validation with attributes is a simple way to check your data using reflection. Here's what you need to know:

Attribute Checks For How To Use It
[Required] Missing data [Required(ErrorMessage = "Name is Required")]
[EmailAddress] Valid email [EmailAddress(ErrorMessage = "Invalid email")]
[StringLength] Text length [StringLength(100, MinimumLength = 6)]
[Range] Number limits [Range(1, 100)]
[Phone] Phone numbers [Phone]

Want to make your own rules? Here's how to check if someone's 18 or older:

public class CustomerDateOfBirthValidation : ValidationAttribute {
    private int MINIMUM_AGE = 18;

    protected override ValidationResult? IsValid(object? value, 
        ValidationContext validationContext) {
        if (!DateTime.TryParse(value?.ToString(), out DateTime dob)) {
            return new ValidationResult("Invalid date format");
        }

        var minDateOfBirth = DateTime.Now.Date.AddYears(MINIMUM_AGE * -1);
        if (dob > minDateOfBirth) {
            return new ValidationResult("Must be 18 or older");
        }
        return ValidationResult.Success;
    }
}

Add it to your model:

public class Customer {
    [CustomerDateOfBirthValidation]
    public DateTime DateOfBirth { get; set; }
}

Need to check if someone clicked "accept"? Use this:

[MustBeTrue(ErrorMessage = "Terms must be accepted")]
public bool AcceptTerms { get; set; }

What makes validation work better:

  • Simple rules
  • Clear messages
  • Null checks
  • Edge case tests
  • Smart caching

Run your validation like this:

var context = new ValidationContext(customer);
var results = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(customer, context, results, true);

That's it! Your validation rules stay clean and you can use them anywhere in your app.

3. Object Mapping Made Simple

Let's break down object mapping in C# - it's just copying data between different objects. Here's what you need to know:

Mapping Approach Speed Use Case Code Complexity
Manual Mapping Fast Simple mappings Low
Reflection Medium Dynamic mappings Medium
AutoMapper Slower Complex mappings Low

Here's a basic property copier using reflection:

public class PropertyCopier<TParent, TChild> 
    where TParent : class 
    where TChild : class {

    public static void Copy(TParent parent, TChild child) {
        var parentProps = parent.GetType().GetProperties();
        var childProps = child.GetType().GetProperties();

        foreach (var parentProp in parentProps) {
            foreach (var childProp in childProps) {
                if (parentProp.Name == childProp.Name && 
                    parentProp.PropertyType == childProp.PropertyType) {
                    childProp.SetValue(child, parentProp.GetValue(parent));
                    break;
                }
            }
        }
    }
}

Want to see it in action? Here's how:

var customer = new CustomerDataModel { 
    Name = "Jane Smith",
    Start = DateTimeOffset.Now
};

var viewModel = new CustomerViewModel();
PropertyCopier<CustomerDataModel, CustomerViewModel>.Copy(customer, viewModel);

Need more control? Add attributes to your properties:

public class CustomerViewModel {
    [MatchParent("FullName")]
    public string Name { get; set; }

    [MatchParent("StartDate")]
    public DateTimeOffset Start { get; set; }
}

"If your AutoMapper mappings are trivial, why not map with trivial code? It's dumb code, but I like dumb code for dumb tasks." - Anthony Steele, Author

Here's what makes mapping work better:

  • Add null checks
  • Handle different types
  • Store property info
  • Test weird cases
  • Keep it simple

Want speed? Use pre-compiled mapping - it's 3x faster than basic reflection. This works great for object-to-dictionary mapping or when you know your types upfront.

Bottom line: Pick what fits. Manual mapping for simple stuff, reflection when you need flexibility, AutoMapper when things get complex.

4. Building Test Frameworks

Test frameworks need three core functions: finding tests, running them, and showing results. Here's how reflection makes it happen:

Task Reflection Method What It Does
Find Tests Attribute scanning Spots [TestMethod] and [Test] markers
Run Tests Runtime execution Calls test methods automatically
Access Code Member access Gets to private and internal code
Test Control Custom attributes Switches features on/off

Here's a simple test runner that shows reflection in action:

public class TestRunner {
    private int _total, _passed, _failed;

    public void RunTests(Assembly assembly) {
        var testClasses = assembly.GetTypes()
            .Where(t => t.GetCustomAttribute<TestClassAttribute>() != null);

        foreach(var testClass in testClasses) {
            var testMethods = testClass.GetMethods()
                .Where(m => m.GetCustomAttribute<TestMethodAttribute>() != null);

            foreach(var method in testMethods) {
                try {
                    var instance = Activator.CreateInstance(testClass);
                    method.Invoke(instance, null);
                    _passed++;
                } catch {
                    _failed++;
                }
                _total++;
            }
        }
    }
}

Need to test private code? PrivateObject makes it simple:

[TestMethod]
public void TestPrivateMethod() {
    var obj = new PrivateObject(typeof(MyClass));
    var result = obj.Invoke("PrivateMethod", 42);
    Assert.AreEqual(expected, result);
}

Want feature flags in your tests? Add a custom attribute:

[FeatureFlagTest("newFeature", true)]
public void TestWithFeatureEnabled() {
    // Test code here
}

"I wanted something simpler, and similar to something like Python decorators, where in we can just use an attribute to decorate the test with desired configuration for the feature flag." - Mitesh Shah, Author

Make your test tools better:

  • Store reflection data to boost speed
  • Set up and clean up tests properly
  • Show clear error messages
  • Keep track of test times
  • Group tests by category

Testing internal code? Add this to AssemblyInfo.cs:

[assembly: InternalsVisibleTo("TestProject")]

This opens up internal code to your test project - no reflection tricks needed.

sbb-itb-29cd4f6

5. Managing App Settings

Here's how reflection simplifies app settings management and cuts down manual coding:

Task Reflection Method What It Does
Load Settings GetProperties() Pulls all settings class properties
Save Settings SetValue() Changes values while app runs
Access Config GetSection() Gets data from appsettings.json
Type Conversion GetType() Manages different data types

Here's a self-managing settings class:

public class Settings {
    public void Save() {
        var composite = new ApplicationDataCompositeValue();
        Type type = this.GetType();

        foreach (var property in type.GetProperties()) {
            string name = property.Name;
            var value = property.GetValue(null);
            composite[name] = value;
        }

        ApplicationData.Current.LocalSettings.Values["AppSettings"] = composite;
    }

    public void Load() {
        var settings = ApplicationData.Current.LocalSettings;
        if (settings.Values.ContainsKey("AppSettings")) {
            var composite = (ApplicationDataCompositeValue)settings.Values["AppSettings"];
            Type type = this.GetType();

            foreach (var property in type.GetProperties()) {
                if (composite.Keys.Contains(property.Name)) {
                    property.SetValue(type, composite[property.Name], null);
                }
            }
        }
    }
}

For ASP.NET apps, here's how to pull settings from appsettings.json:

var appSettings = _configuration.GetSection("EmailSettings")
    .AsEnumerable()
    .Where(x => x.Value != null)
    .ToDictionary(
        x => x.Key.Replace("EmailSettings:", ""), 
        x => x.Value
    );

5 ways to boost settings performance:

  • Cache reflection data
  • Add type validation
  • Set fallback values
  • Switch to XML/JSON format
  • Handle data errors

Need settings on the fly? Try this:

public static T GetSetting<T>(string key) {
    var property = typeof(Settings)
        .GetProperty(key, BindingFlags.Public | BindingFlags.Static);
    return (T)property.GetValue(null);
}

This code lets you grab settings by name - no type knowledge needed.

Making Reflection Faster

Here's what happens when you compare direct method calls to reflection:

Method Type Execution Time Performance Ratio
Direct Call 127 ms 1x
Reflection Call 3.5 seconds 30x slower
Cached Reflection 95 ms 33x faster than uncached

Want to make reflection run faster? Here's how:

1. Cache Everything

Put your Type, MethodInfo, and PropertyInfo lookups in a dictionary:

private static Dictionary<string, PropertyInfo> _propertyCache 
    = new Dictionary<string, PropertyInfo>();

public static PropertyInfo GetCachedProperty(string name) {
    if (!_propertyCache.ContainsKey(name)) {
        _propertyCache[name] = typeof(MyClass)
            .GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
    }
    return _propertyCache[name];
}

2. Use Delegates

Turn reflection calls into delegates:

var method = typeof(List<int>).GetMethod("Add");
var del = (Action<List<int>, int>)Delegate
    .CreateDelegate(typeof(Action<List<int>, int>), method);

3. Apply DynamicMethod

When you need the BEST performance:

var dynamicMethod = new DynamicMethod(
    "FastPropertyGet",
    typeof(object),
    new Type[] { typeof(object) },
    typeof(PropertyAccessor).Module
);

Here's what happens with 5000 items:

Implementation Time (μs) Memory Usage
Base Code 2,809.60 Baseline
Raw Reflection 281,215.52 High
Cached Reflection 95,066.90 Medium
Dynamic Method 3,012.45 Low

"The point of the original speed comparison is to show that using Reflection to call a method is 30 times slower than making a direct call." - Jeremy Bytes, Author and Presenter

To keep your code fast:

  • Load types when your app starts
  • Build delegate caches right away
  • Switch to interfaces after loading
  • Don't use reflection for code that runs often
  • Check your speed with big data sets

Tips for Using Reflection

Here's how to make reflection work better in your code:

Area Key Tips
Security - Check all input before using reflection
- Keep MethodInfo parameters private
- Set up proper permissions in your app domain
Performance - Pre-compile expressions you'll use often
- Store PropertyInfo and FieldInfo objects
- Pick generated methods over reflection calls
Error Handling - Add try-catch blocks to reflection code
- Look for null values before member access
- Make sure types match before casting

Block Unwanted Reflection

Here's code to stop reflection when you don't want it:

public void EnsureNotCalledFromReflection()
{
    var stack = new System.Diagnostics.StackTrace();
    if (stack.ToString().Contains("System.Reflection"))
    {
        throw new InvalidOperationException("Reflection access not allowed");
    }
}

Speed Test Results

Operation Type Time (μs) Memory Impact
Direct Property Access 0.305024 Minimal
Expression Compilation 44.375068 Medium
Delegate Creation 0.3465711 Low

Keep It Safe

// Make sure code has permission to use reflection
if (!assembly.IsFullyTrusted)
{
    throw new SecurityException("Untrusted assemblies cannot use reflection");
}

What Not to Do

  • Run reflection code over and over without saving results
  • Skip security checks
  • Forget to check types
  • Leave out error handling
  • Use private members without thinking it through

"Only trusted code can use reflection to access security-critical members starting from .NET Framework 4." - Microsoft .NET Documentation

Document Everything

Element What to Write Down
Purpose Why you picked reflection over direct calls
Security What permissions your code needs
Performance How you'll save results for speed
Maintenance Ways to check types and handle errors

The numbers show why smart reflection usage matters:

  • Pre-compiled expressions run 20 times faster than Activator.CreateInstance
  • Property access with pre-compiled expressions beats PropertyInfo.SetValue by 50 times
  • Saving delegates makes your code 10 times faster than basic reflection

Summary

Here's what matters most about .NET Reflection:

Application Main Benefits Common Uses
Plugin Systems Load code on demand Add features without rebuilding
Data Validation Check data automatically Read attributes, apply rules
Object Mapping Switch between types Map DTOs, handle JSON/XML
Test Frameworks Look inside code Find tests, set up mocks
App Settings Handle settings Read/update properties

Speed and Memory

Method Speed Memory
Direct Access 0.3 μs Tiny
Cached Reflection 0.35 μs Small
Raw Reflection 44.37 μs Medium

Make It Work Better

  • Store reflection results - don't look things up twice
  • Add error handling for missing stuff
  • Check permissions for private members
  • Use binding flags to search smarter

"Using reflection efficiently is like haggling with an API. You have to pay to play and make some concessions. But it's worth it." - Joel Pobar, Program Manager, Microsoft CLR team

Stay Safe

  • Set app domain permissions right
  • Look at assembly trust
  • Clean input before reflection
  • Keep method info private
  • Use try-catch blocks

Want more .NET tips? Get the .NET Newsletter.

Bottom Line: Use reflection when you need to check types or change stuff while your code runs. If you know what you need at compile time, just write regular code instead.

Learn More

Here's what you need to know about .NET Reflection tools and performance:

Library What It Does Speed Boost
Fasterflect Makes reflection faster + clones objects 5-100x faster
Soenneker.Reflection.Cache Speeds up common operations Up to 24,842% faster
Namotion.Reflection Handles XML docs + nullable types -

Speed Comparison

Method Performance When to Use
Pre-compiled Expressions 20x faster High-volume operations
Cached Reflection 3-6x faster Regular reflection
Expression Compilation 44.37 μs Property operations

Real Performance Numbers

Operation Standard Speed Cached Speed
Get Type 1,022.30 ns 17.52 ns
Get Methods 256.526 ns 1.030 ns
Get Members 550.2334 ns 0.6579 ns

Here's what makes reflection FAST:

  • Cache your reflection calls
  • Use pre-compiled expressions
  • Keep an eye on performance
  • Test your code speed

Want to stay updated? Check out the .NET Newsletter.

"Performance matters in .NET apps. The right tools make all the difference." - Mo Mulla, Founder of Parental Questions

Key Resources:

FAQs

What is reflection in .net with an example?

Reflection in .NET is a way to peek inside your code while it's running. Think of it like X-ray vision for your programs - you can see and work with parts that are usually hidden.

Here's what reflection lets you do:

What You Can Do How It Works Code Example
Look Up Types Check what kind of data you're working with int i = 42;
Type type = i.GetType();
Make New Objects Create objects without knowing their type beforehand var obj = Activator.CreateInstance(Type.GetType(assemblyName));
Call Methods Run methods by their names method.Invoke(instance, null);
Access Hidden Data Read or change private stuff field.GetValue(instance);

Want to see reflection in action? Here's a simple example that calls a private method:

using System.Reflection;

var instance = new Target();
var method = typeof(Target)
    .GetMethod("GetSecret", 
        BindingFlags.Instance | 
        BindingFlags.NonPublic);

var result = method.Invoke(instance, null);
// Output: "42"

This code shows how reflection helps you:

  • Test private methods
  • Build plugin systems
  • Create frameworks
  • Handle types that change while your code runs

It's like having a master key that opens any door in your code - just remember to use it wisely!

Related posts

Read more