---
title: "Reflection"
date: "2026-04"
updated: "2026-09"
category: "Design Patterns & Architecture"
tags: ["Design Patterns & Architecture"]
description: "Reflection looks a bit like a delegate in places, but the two are pretty different. The point of a delegate is compile-time safety..."
source: "https://enriquemark.com/en/posts/reflection/"
---

Reflection looks a bit like a [delegate](/en/posts/delegates/) in places, but the two are pretty different. The point of a delegate is compile-time safety. Reflection gives up the compile-time checks and gets runtime flexibility in return: it calls or gets a given object by string, which lets a program read or change any code at runtime. Reflection is essentially a "meta operation", code operating on code, which is also what "reflection" literally means.

Take the scheduling case I wrote about before: say my scheduler works by subclassing, and the subclass only cares about the concrete job. But if what I need is to figure out how many jobs inherit the base class, the current approach is to scan with reflection. Right after I first learned how delegates work, my idea was, could the base class do lifecycle management, wrap the subclass in a run, so what the scheduler actually runs is run, then have the base class declare an event, and this run is the—and this is where it hit me—the delegate property predefined in the base class gets reset every time it's inherited and instantiated, and then the event queue is empty.

So at startup you scan the program itself to see how many subclasses inherit this base class, and only then can you hand them to the scheduling framework to register in one place. Otherwise, to know how many jobs there are you have to get the jobs first, to get the jobs you have to register them first, and to register them you have to know how many jobs there are, and in the end no job gets registered. The framework has to know the count before instantiation, so there's no way I can wait until after that to put jobs into the event, it has to be known before startup, and the only way is reflection scanning for classes that inherit base. This is a type discovery problem, not a problem of interaction between the objects a delegate carries (a subscriber operating through a delegate the publisher exposes).

By the way, there's one place where reflection is used a lot like generics, and it's even more flexible. At compile time, for instance, I don't have to specify the type at all, and instead take whatever type I'm given at run time. That has a cost. If I write `where T : IConverter`, at least I've specified what the interface structure looks like, and if I get it wrong the compiler tells me. With reflection all I can do is a cast. I'm agreeing with you that it needs to be this type here, but whether you actually use that type is something I can't enforce (the interface below comes from [Inversion of Control](/en/posts/inversion-of-control/)):

```C#
Type type = Type.GetType("MyApp.XmlConverter");
IConverter converter = (IConverter)Activator.CreateInstance(type);
// If XmlConverter doesn't implement IConverter at all, this throws InvalidCastException right here
```

Here, if what comes in doesn't match the contract, you only find out at run time. With a generic the compiler errors out and tells me, and it never gets to that last stage. So reflection does have a bit of a dynamic-language flavor to it, like Python, where you don't know whether it will throw until it runs. That's a trade-off between flexibility and stability. Some situations do need the flexibility, but you have to know its cost and its boundaries. If you can, wrap the errors around that flexibility up front. Even if the result is unpredictable, keep the damage contained and as small as possible.

---

**Translation note.** I wrote this in Chinese. This English version is an LLM translation, so the wording is not mine even though the thinking is. Original: [反射](/zh-hant/posts/reflection/).
