Skip to Content
Course content

93: Reflection API Basics

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was working on a data-import tool that had to handle dozens of different CSV formats from different clients. Initially, I wrote a separate mapper for every single file type. It worked, but it was a maintenance nightmare. Every time a client added a column, I had to recompile the whole project. I remember staring at the code and thinking, "There has to be a way for the program to just look at the destination class and figure out where the data goes on its own." That's when I really leaned into the Reflection API. Instead of hard-coding client.Name = row[0], I wrote a loop that asked the class, "What properties do you have?" and then matched those names to the CSV headers. It turned a thousand lines of repetitive mapping into about twenty lines of generic logic.

Peering into the Type System

At its core, Reflection is the ability of your code to inspect its own metadata at runtime. In C#, this all revolves around the System.Type class. You can get a Type object either by using the typeof operator if you know the class at compile time, or by calling the GetType() method on an existing instance of an object.

Once you have that Type object, you can start digging. You can ask for all the methods, the fields, the constructors, or the properties defined in that class. For example, if you're building a system that needs to automatically discover settings in a configuration class, you can use GetProperties() to iterate through everything the class exposes. I often use this when building custom debuggers or serialization logic where I don't know the exact shape of the object I'm dealing with until the app is actually running.


public class GameCharacter
{
    public string Name { get; set; } = "Hero";
    public int Health { get; set; } = 100;
    public void Heal(int amount) => Health += amount;
}

// ... inside a method ...
GameCharacter player = new GameCharacter();
Type type = player.GetType();

foreach (var prop in type.GetProperties())
{
    Console.WriteLine($"Property: {prop.Name}, Type: {prop.PropertyType}");
}

Dynamically Interacting with Objects

Inspecting the metadata is useful, but the real power comes when you actually do something with that information. Reflection allows you to get or set property values and even invoke methods dynamically. This is where things get a bit "magic"—and a bit dangerous. When you use GetValue or Invoke, you're bypassing the usual compile-time type checking. If you misspell a method name as a string, the compiler won't warn you; your app will simply crash at runtime.

To change a value, you use PropertyInfo.SetValue. To run a method, you use MethodInfo.Invoke. You'll notice that these methods take an object as the first argument; this is because the Reflection API needs to know which specific instance of the class it should be operating on. I've used this technique to implement "Command" patterns where a string from a text file or a network packet tells the program which method to execute.


// Let's call the 'Heal' method by its string name
var method = type.GetMethod("Heal");
method.Invoke(player, new object[] { 20 }); 

Console.WriteLine($"New Health: {player.Health}"); // Output: 120

The Cost of Runtime Discovery

I should be honest with you: Reflection is slow. When you call a method normally, the JIT compiler optimizes that call. When you use Reflection, the CLR has to search through metadata, verify security permissions, and box/unbox arguments. If you're doing this once when an application starts up to load a configuration, you'll never notice. If you're doing it inside a for loop that runs 10,000 times per second in a game loop, your frame rate will tank.

If you find yourself needing the flexibility of Reflection but the speed of direct calls, look into Delegates or Compiled Expressions. But for the vast majority of administrative tasks—like building an automated testing suite or a dynamic UI—the standard Reflection API is more than enough. Just remember to cache your PropertyInfo or MethodInfo objects if you plan on using them repeatedly; there's no need to ask the system for the same metadata twice.




📋 Practical Task

Exercise: The Automated Property Auditor

You are tasked with creating a "Security Auditor" tool. In your project, some classes have properties that are considered "Sensitive" (e.g., Password, SocialSecurityNumber, SecretKey). Instead of manually checking every class, you need to write a method that uses Reflection to find any property whose name contains the word "Secret" or "Password" and prints a warning to the console.

Requirements:

  • Create a class called UserAccount with properties: Username, Email, Password, and SecretAnswer.
  • Write a method called AuditProperties(object obj) that takes any object as an argument.
  • Inside AuditProperties, use Reflection to iterate through all public properties of the object.
  • If a property name contains "Password" or "Secret", print: "Warning: Sensitive property [PropertyName] detected!"
  • Instantiate a UserAccount and pass it to your auditor to verify it flags the correct properties.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.