What is reflection?
Reflection is a way of obtaining metadata information about the types used in applications which in turn helps you with describing the types, modules, or assemblies at runtime. This is an extremely useful feature in the strongly typed languages like C#.
When to use reflection?
The main use of reflection is to inspect assemblies, types, and members of a class. It can be used whenever you need to determine the contents of an unknown assembly, get all its dependencies or invoke methods by name.
The use of reflection is not recommended due to its bad performance because of all the security checks being done when calling a method or iterating through an object’s members.
Example
Let’s take the class defined below.
public class MyClass
{
public int Number { get; set; }
}
It is a simple class which has one integer property. We will use this class to create 10000000 instances and test the performance of reflection on them. Let’s first take a direct access method of getting and setting the property as in the code below.
List<MyClass> myClassList = Enumerable.Repeat(new MyClass(), 10000000).ToList();
object aux = 0;
foreach (var obj in myClassList)
{
aux = obj.Number;
obj.Number = 3;
}
The above code executes in ~153ms which is pretty fast. But what if we want to invoke the “Number” property by its name? This is where Reflection is going in.
List<MyClass> myClassList = Enumerable.Repeat(new MyClass(), 10000000).ToList();
object aux = 0;
PropertyInfo info = typeof(MyClass).GetProperty("Number");
foreach (var obj in myClassList)
{
aux = info.GetValue(obj);
info.SetValue(obj, 3);
}
The above code is doing the same operations as the previous, but the difference is that we access the “Number” property by its name. As you can see, we are extracting and using the reflected property type to access the “Number”.
The bad part with the above code is that it executes in ~5196 ms, which is enormously larger than direct access. The reason is that the invoking is doing more tasks when Reflection is used.
- Check if the called method exists
- Security and visibility checks
- Signature checks to see that the number of provided parameters and their types matches with the defined signature of the method
- Unwraps the parameters
The above checks are done with every GetValue and SetValue call.
Boosting up the performance of Reflection
The main problem with Reflection is that the security is verified at every call. Fortunately, there is a way to make the security checks only the first time, and at the second method call, we can consider the method “trusted” and call it normally. This is accomplished using Delegates.
List<MyClass> myClassList = Enumerable.Repeat(new MyClass(), 10000000).ToList();
object aux = 0;
Action<MyClass, int> setter = (Action<MyClass, int>)Delegate.CreateDelegate(typeof(Action<MyClass, int>), null, typeof(MyClass).GetProperty("Number").GetSetMethod());
Func<MyClass, int> getter = (Func<MyClass, int>)Delegate.CreateDelegate(typeof(Func<MyClass, int>), null, typeof(MyClass).GetProperty("Number").GetGetMethod());
foreach (var obj in myClassList)
{
aux = getter(obj);
setter(obj, 3);
}
The above code creates strongly typed delegates for the get and set property methods. This way, we are forcing all the security checks to be done when the delegates are created.
Results? The code using delegates is executing in ~161ms. This result is comparable to direct access, which was ~153ms.
Conclusion
The advantage of using Reflection is very big, and you can do a lot with it, from exploring assemblies to dynamically call methods and interact with them. However, if the use of reflection is abused, severe performance issues might occur in the application. Consider the fact that in the above example, there was a single property to get/set. The time is visibly increasing when you have more property/methods to access, and the creation of delegates could prove a very good solution.