Denis Jakus

Demystifying Tech & Management

CTO x Unique People

AI / ML / ChatGPT

Unreal Engine

Low Code

Management & Leadership

IT Consultant

Denis Jakus

Demystifying Tech & Management

CTO x Unique People

AI / ML / ChatGPT

Unreal Engine

Low Code

Management & Leadership

IT Consultant

CTO Note

Real example of reflection5 min read

16 July 2019 .NET
Real example of reflection5 min read

I got asked via email if I could write about reflections in C#.
So I decided to postpone “Part 2 of Level-up your Xamarin apps”, and will head straight to writing about reflections.

What is REFLECTION in C#?

In nutshell, it’s a namespace (System.Reflection).

Google source

That namespace contains classes that allow you to obtain all sorts of data about assemblies, modules or types at runtime or programmatically.

Using reflection, you can get the kind of information that you will see in the Class Viewer, Object Explorer, or a Class Explorer.

So, we use reflection to dynamically create an instance of a type, get the type, invoke its methods or access its fields and properties.

Building blocks of Reflection

Building blocks of System.Reflection namespace are:

  • Assembly
    • allows you to load, investigate, and manipulate an assembly
  • Module
    • allows you to access a given module within a multifile assembly
  • AssemblyName
    • allows you to details behind an assembly’s identity
  • EventInfo
    • allows you to details for a given event
  • MemberInfo
    • used to obtain information about all members of a class (constructors, events, fields, methods, and properties)
  • MethodInfo
    • used to get information about method
  • ParameterInfo
    • used to get information about parameter
  • PropertyInfo
    • used to get information about property
  • FieldInfo
    • used to get the attributes of a field and provides access to field metadata

System.Type

Type is the core of Reflection functionality.

One thing which is necessary to mention is that System.Reflection is highly coupled with System.Type.

Type is the core of Reflection functionality.

We use the members of Type to get access about a type.

In it’s simplest form we use Reflection using Type like this:

int number = 13;  
System.Type type = number.GetType();  
Console.WriteLine(type); // outputs System.Int32

Since there are a bunch of good tutorials about reflection, I will just link here a few, of what I have found to be, useful ones:

Why should we use Reflection?

What I will write a little more about, here, is where reflection is useful.
As-well why should one use reflection in the real world, as I find examples lacking regarding this topic?!

Real world example of Reflection in C#

So I will give a straight example.

Are you using .NET Core for writing backend? Do you have layered architecture? If so, I guess, that you know how tedious it is to write this line of code each time:

services.AddScoped<IServiceA, ServiceA>();
services.AddScoped<IServiceB, ServiceB>();
services.AddScoped<IServiceC, ServiceC>();
.
.
.

And each time we hope, we didn’t forget to include our newest ServiceXYZ in Startup.cs file.

Automate everything you can

Would it be much easier if we could just write our code once and all of our services “automagically” loaded itself!? Wouldn’t that be fun? Well, of course, it would! Who doesn’t like automation!?!?

So to ease our life a bit, we will create an extension (more about extensions here) to our Startup.cs class regarding RegisterService method and write our code like this:

public static IServiceCollection RegisterServices(this IServiceCollection services, IConfiguration configuration)
{
	var serviceTypes = new List<Type>();

	const string servicesNamespace = "BLL.Services";
	const string interfacesNamespace = "BLL.Contracts.Services";

	foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
	{
		serviceTypes.AddRange(assembly.GetTypes()
			.Where(t => t.Namespace != null
						&& t.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) == null
						&& (t.Namespace == servicesNamespace ||
							t.Namespace == interfacesNamespace)).ToList());
	}

	foreach (var intfc in serviceTypes)
	{
		if (intfc.IsInterface)
		{
			var impl = serviceTypes.FirstOrDefault(c => c.IsClass && intfc.Name.Substring(1) == c.Name);
			if (impl != null) services.AddScoped(intfc, impl);
		}
	}
}

Voila!

No more tedious adding of services, line by line!
It all works out-of-the-box. You just create your services in other BLL assembly and using reflection you auto load them inside Startup.cs file.

Great!

Another example I will state here shall be without code. I’ll just give you an example for real-world usage.

Imagine you have developed, or are still developing, software for banking or telecom industry. You have it on the production and it’s working fine.
And changes are happening regularly (i.e. every few months or so).

Instead of hardcoding your functionalities for all sorts of computations inside the core app and rebuilding your software and then installing(publishing) it.

Wouldn’t it be much easier to have a separate DLL which you would load into system and then using reflection access its props or methods and use them dynamically!?

Fictional part of banking/telecom web application

Of course, it would be much easier!

This way we don’t have to bother with recompiling the app. Instead we just ship our DLL as plugin and let the app user (or admin) upload our DLL via an app. And then have our logic do the stuff it has to do after uploading this plugin!?!

The end

I do hope, you did get a clearer picture of reflection after reading this post. I tried to sum it up and be concise as much as I could be.

If you have any further thoughts or questions, just ask me via any social network I’m currently on. I prefer LinkedIn or Twitter, though.

Happy reflectioning! 🙂

Taggs:
Related Posts
How to organize Visual Studio projects

Facebook Twitter Evernote Hacker News Gmail Print Friendly Yahoo Mail reddit LinkedIn Blogger Tumblr Like In my latest Youtube video,…

Caching in DotNet Core WebAPI using Strategy Pattern

Facebook Twitter Evernote Hacker News Gmail Print Friendly Yahoo Mail reddit LinkedIn Blogger Tumblr Like Let me write today about…

3 Comments
  • igoragm 17:37 06 December 2020 Reply

    Thank you, good article.
    Just knowing this concept helped me a lot even in JavaScript where rudimentary but still useful form of reflection exist (Reflect built-in object).

  • Johan 12:36 30 August 2021 Reply

    Pretty cool. Sadly, you need to add the using, otherwise it will be deleted by the compiler. I removed all my registration in the startup and Visual Studio already told me the usings are unused, but I left them in my code. I ran your code, but the services and interfaces are not found anymore. When I register just one interface and service (usings are now being used) it works perfectly… Any way to fix this?

    • Denis Jakus 13:15 30 August 2021 Reply

      Hi Johan,

      my guess is that your classes are not following your naming conventions.

      So, we have code like this:

      #region Automatically Register Services under ‘App.BLL.Services’ namespace

      AutomaticallyRegisterServices(services, “App.BLL.Services”, “App.BLL.Contracts.Services”);

      #endregion

      ///

      /// By using reflection, loads assemblies and registers each service/repository interface with it’s own implementation
      ///

      /// IServiceCollection
      /// Project assembly with real implementations
      /// Project assembly with contracts/interfaces
      private static void AutomaticallyRegisterServices(IServiceCollection services, string servicesNamespace, string interfacesNamespace)
      {
      var serviceTypes = new List();

      foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
      {
      serviceTypes.AddRange(assembly.GetTypes()
      .Where(t => t.Namespace != null
      && t.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) == null
      && (t.Namespace == servicesNamespace ||
      t.Namespace == interfacesNamespace)).ToList());
      }

      foreach (var intfc in serviceTypes)
      {
      if (intfc.IsInterface)
      {
      var impl = serviceTypes.FirstOrDefault(c => c.IsClass && intfc.Name.Substring(1) == c.Name);
      if (impl != null) services.AddScoped(intfc, impl);
      }
      }
      }

      And you should take care of your naming namespaces/classes right, in order this to work as described.

      I.e:

      Your real implementation class:

      service namespace: App.BLL.Services
      class name: ExampleClassService (which inherits from the interface: IExampleClassService)

      Your contract:

      interface namespace: App.BLL.Contracts.Services
      interface name: IExampleClassService

      This should be enough to work as-is, since I’ve had no issues at all.

      Ping me if I’ve helped you and thnx for reading.

Write a comment