There may be instances in software applications, whereby there is a requirement to get a list of classes in an application that implement a specific interface and do something with them.
In my instance, I needed to retrieve a list of Entity Classes. In the application, all Domain Entity classes implement a common
Although initially it may seem a little heavy handed to enforce your
POCO classes to inherit and implement an interface, there are also some good reasons to do this.
In this post, I will provide an example of how to use reflection to retrieve a list of classes that implement a specific interface and then execute a method on that interface.
Commonality of data
In most applications you may require some commonality of data, for instance, every Attribute may require a
Name, Description, Added Date, Modified Date or even if it is visible in certain sections of your of application code. Although these names my be different in your data layer i.e. they map to different field names in your database, you may want to have consistency in your application layer.
In some instances, you may just want to add an Empty Marker Interface to provides a mechanism associate metadata with a class where the language does not have explicit support for such metadata, or you need to ensure that a classes implement a specific method
A side benefits of this type of approach, is that it provides a mechanism for developers to identify the purpose of a class during development. For Instance, if you notice that a class implements the
IDomainEntity interface, you can immediately identify that the
class is a domain entity and from there will be able to deduce which layer it is implemented in, without having to search all references etc. In some large code bases this approach my help to reduce complexity and cognitive load.
These practices my typically be defined within an organisations Coding Conventions, for particular products or environments. In may on the outset seem heavy handed or even wasteful but on occassion it can help to eliminate ambiguity of
POCO (Plain Old C# Classes) classes throughout an application stack.
The .net framework does provide support for
Custom Attributes on classes, so you may want to consider this approach if you don’t want to be bogged down by marker interfaces. Using Custom Attributes is beyond the scope of this article, but they are equally helpful and alternative solution to the one described in this post.
In trying to come up with a problem domain, in which to use this type of solution, I came up with a scenario in which you may need to store Reference Entities within a Redis Cache store. You may need to store Session Time Outs on your Redis keys, and these may be configured for couple of hours. You may have some kind of process whereby when the Key times out, the cache is refreshed and all is good with the world. This is a typical approach using Factory Method pattern
On occasion during the course of the day, we may wan to flush the cache and reload it. This may need occur during the deployment process of the application to production. Your Build or deployment scripts may be configured to call an API end point to clear specific or all Domain Reference Entity Caches.
There may be a requirement to build in flexibility to flush all entities without the need to pass in a managed list of items and just ask the application itself to determine the list of entities that require flushing. It is entirely possible that new entities may have been added during the course of development or even in the previous deployment, so creating a managed list will be difficult. So there may be need to create a list dynamically using reflection to to scan the binaries to get all references of classes which implement specific interface.
Power of reflection
It’s in situation like these that the power of Reflection comes into play.
C# and Reflection in the .net framework are powerful tools to have in your arsenal, to solve a scenario such as this.
In a nutshell, reflection is when managed code has the ability to read its own metadata to find out information about the Assemblies, Modules and Types, it is composed of. Using reflection, developers can create an instance of a type and bind that type to an existing object. They can also get the Type from an existing object and access its properties. Using Reflection you can inspect the contents of an assembly.
The classes in the
System.Reflection namespace, together with
System.Type, enable you to obtain information about loaded assemblies and the types defined within, such as
class, interface & value types.
The Common Language Runtime (CLR) loader manages application domains, which constitute defined boundaries around objects that have the same application scope, including loading each assembly into the appropriate application domain and controlling the memory layout of the type hierarchy within each assembly.
Assemblies contain modules, modules contain types and types contain members. Reflection provides objects that encapsulate assemblies, modules and types.
You can use reflection to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object. You can then invoke the type’s methods or access its fields and properties.
Reflection in C#
Having a broader understanding of the capabilities of Dynamic Programming in the .net framework is certainly advantageous to your software development career.
System.Reflection namespace contains classes that allow you to obtain information about the application and to dynamically add
types, values, and objects to the application.
You can compare Reflection in .net framework to C++RTTI (Runtime Type Information), except that it has far richer capabilities. a C# program that uses reflection, uses either the
TypeOf operator or the
GetType() method to get the object’s type.
In the code below, I basically wrote a method that returns a list of strings containing the names of all classes implementing the
The AppDomain Represents an application domain, which is an isolated environment where applications execute.
AppDomain instances are used to load and execute assemblies (Assembly).
The list above can then be used by our method to flush the entities. We again use the power of reflection to retrieve a list of Common Data repositories defined in the Inversion Of Control container , in our case we’re making use of Simple Injector to return the corresponding common data repository with the flush cache interface implemented
Reflection in .net
The base for reflection is the
System.Type class, which is an abstract class representing a type in the Common Type System (CTS). The CTS class, enables the discovery of types used in a module and namespace and also determine if a given type is a reference or value type.
You can parse the metadata tables to search:
In the examples above we used reflection to extract information from the application during runtime, and then to preform actions based on that data recieved.
Use cases for reflection
- Get all global and non-global methods defined in an application.
MethodInfoto extract information such as parameters, name, return type, access modifiers and implementation details.
EventInfoto discover event-handler data type, the name, declaring type and custom attributes.
ConstructorInfoto get data on the parameters, access modifiers, and implementation details of a constructor.
Assemblyto load modules listed in the assembly manifest.
PropertyInfoto get declaring type, reflected type, data type, name and writable status of a property or get and set property values.
CustomAttributeDatato find information on custom attributes or review attributes without having to create instances.
The article above illustrates just how easy it is to use reflection to get information from your application regarding which classes implement specific interfaces.
It also illustrates how you can use reflection to determine which dependencies have been configured in your Inversion of Control (IOC) container.
- How to use Github actions to build & deploy Github nuget packages - October 14, 2021
- How to implement cross cutting concerns with MediatR Pipeline Behaviours - October 5, 2021
- Understanding the difference between Queue and Stack Data Structure - September 22, 2021