C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
93: Reflection API Basics
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
UserAccountwith properties:Username,Email,Password, andSecretAnswer. - 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
UserAccountand pass it to your auditor to verify it flags the correct properties.
There are no comments for now.