Reflection’s often talked about in the context of object-oriented programming. You often use reflection to discover codebase entities at runtime. The language’s reflection API will let you inspect classes, methods, properties, and types from within your system. This lets you build more dynamic functionality.

Systems which utilize reflection are able to interrogate and modify their own environments. This is where reflection differs from plain value introspection. A language with full reflection support will permit codebase modification at runtime, effectively allowing the source to rewrite aspects of itself.

An Example of Reflection

One common use for reflection is during testing. Reflection can help you mock classes by exposing their internal behaviors. A class method that’s protected or private wouldn’t usually be testable; using reflection, you can override the visibility constraint so it becomes public in your unit tests.

In this example using PHP, the Test class defines a protected method that’s used internally. As the method’s performing a calculation, you might want to unit test it. You can’t call the method externally but PHP’s Reflection API lets you bypass the visibility constraints. A ReflectionMethod instance provides information about the method and lets you invoke a modified version.

While this is helpful, you should be conscious that it can be misused. Widespread use of reflection in testing is often indicative of bigger problems in your codebase. It implies the class’ interface is overly restrictive and unsuited to its responsibilities. In many cases, it’s more appropriate to refactor the protected method out into a new class that exposes its own public interface.

Here’s how that might look for the example shown above:

The calculator component is now its own standalone unit with a testable public interface. This goes hand-in-hand with dependency injection – the Test class is now given a Calculator which implements the calculation logic.

Using Reflection With Unpredictable Values

Reflection’s also useful when you’re writing generic code within a framework. You might need to interface with user-supplied types which you can’t anticipate. Reflection can help when you don’t know what methods and properties a type exposes.

You can obtain a picture of the type’s functionality without any previous knowledge of its source. This is useful in the context of logging and error reporting components which might want to dump the member list of any class they’re passed.

Data marshalling systems are often implemented in this way too. Imagine a marshaller that takes a class and converts it to a JSON representation. You might define a convention that any method that’s prefixed with get and ends with Json (e.g. getUserJson()) should be called by the marshaller and added to its output. Reflection provides the mechanism to get the list of methods. You’d then implement logic to identify the ones you should call.

Reflection, Compilation, and Assembles

Reflection provides additional capabilities in compiled languages that utilize linked libraries and assemblies. Reflection APIs let you inspect the contents of loaded assemblies. In languages such as C#, you can dynamically load additional assemblies by utilizing Reflection APIs.

This approach can be useful if you’re implementing a plugin system with user-supplied assemblies. Your program won’t know which plugins are available when it’s compiled. Each time it launches, it’ll need to check the filesystem to find available plugin assemblies. Once a plugin’s been found, Reflection provides a mechanism to load and instantiate its members.

You could inspect the plugin, find the classes it provides and register it with your application. Further inspection of the assembly might provide the plugin’s name and version for display in your logs and user interface.

Using reflection, you could offer a “storage driver” key in your system’s configuration file. The appropriate assembly would be loaded dynamically based on your config file’s value. You’d inspect the assembly to discover the class that implements your StorageDriver interface.

This approach lets you switch out components of your system at runtime. You don’t need to recompile or restart your program to change between assemblies. This gives you increased flexibility and assists the implementation of configuration directives.

Eval Statements

Reflection is closely related to eval. Many programming languages provide a way to execute dynamic string values as source code.

Eval is a permutation of reflection with almost limitless power. It lets you create and run new code within a live program. This poses a potentially catastrophic security issue if user input gets fed into the eval string.

An eval statement should be used when you’re left with no other options. You need to make sure the statement is executed in a restricted context which can’t be exploited by users. Remember that a successful code injection would give an attacker the same powers as your regular application code.

Conclusion

Reflection is a programming technique that gives code introspective abilities. Effective use of reflection lets you write more dynamic systems and benefit from increased automation. You can also use reflection to unit test otherwise unreachable private code.

You do need to exercise caution. Reflection APIs in programming languages are powerful so with them comes responsibility. The biggest issue is reflection’s ability to subvert the protections provided by your programming language.

Reflection permits the existence of scenarios that would otherwise be impossible, such as writes to “immutable” variables and widespread public use of private methods. You should be able to trust your code to respect the rules of its language. Use of the reflection APIs must therefore be carefully considered and scoped to specific sections of your system.