This article is first part of the series of three. Next ones you can find here and here.
Code for all three articles, with new features and bug fixes is available on GitHub and as Nuget package.
Why not Reflection?
If you working with .NET and C# (probably other languages too), at some point, you will have to write more advanced and generic code, that i.e. reads all properties of a type, searches for attributes, etc. Or maybe you want to call some private method or property? It is not that rare to use some internal mechanism of framework or library. Usually, the first solution involves Reflection. But the thing is: Reflection is slow. Of course, it is sufficient if you need to use Reflection rarely and it is not the main code of the application. If it is used more often at some point, you will probably try to optimize it. And now is the fun part: how to do it?
The main problem with Reflection is that it is slow. It is much slower then calling members of types directly. If you do this via Reflection, everything slows down. If you need to create an instance of a type, you probably will use Activator.CreateInstance method. It is a little quicker than reflection, but is also slower then calling constructor directly. For properties, methods and constructors, it is possible to use expressions (Expression type static methods), but expressions and compiling them is even slower than reflection.
When I was looking for a fast way to create types instances in this article, I found this question on Stack Overflow. According to it, the fastest way to create an instance is to use delegate created by compiling expression. This made me think about the general mechanism for acquiring instances and members of types via delegates. After all, it is a point of interest for me lately since I work on Xamarin applications with PCL projects that have portable .NET Framework with less features then the full version. Fast mechanism for returning not available members of types would be really nice and would make PCL code bigger at the expense of platform code (which is a good thing). And since you can create delegates from Reflection, Expression or lambdas and there is just a little overhead (since you have called one more method), the mechanism for creating delegates for acquiring all members of a type (public and private) is a good idea. And this is the main topic of this article.
The following code is written in Visual Studio 2015 with use of .NET 4.5, but it is possible to port most of the code to older versions.
Delegates for everything
Type can have the following types of members:
- Static
- Properties
- Methods
- Fields
- Events
- Constant fields
- Instance
- Properties
- Methods
- Fields
- Constructors
- Events
- Indexers
We can create delegates for retrieving all of these members. Frankly, not all of those delegates make sense. Delegates for constants for example. Those fields do not change, so there is no point of retrieving them more than once. You can easily retrieve those values using Reflection and save it somewhere for future use.
The only thing this article will not cover is static events. Did you ever use one? Seriously, I cannot think of a case when this is absolutely needed. Much more common would be to create ordinary event and invoke it from a singleton instance. The good thing is that it is pretty easy to create a delegate for a static event with just a small change for instance event.
Static Properties
Properties and methods are the most common members. Methods are much more complex so it is better to start with properties.
I think the most common case for reflection is retrieving set or single properties from instance or type. It may be for dynamic table views, serialization, deserialization, saving to file, etc. Static properties are little less complex, so we will start with them instead of instance ones.
Get Accessor
We should create a new project. It will be called Delegates – console application for easy tests.
The first thing to do is to create class for tests. We can name it TestClass.
public class TestClass { public static string StaticPublicProperty { get; set; } = "StaticPublicProperty"; internal static string StaticInternalProperty { get; set; } = "StaticInternalProperty"; protected static string StaticProtectedProperty { get; set; } = "StaticProtectedProperty"; private static string StaticPrivateProperty { get; set; } = "StaticPrivateProperty"; }
The second thing is to create a class for delegates creation – DelegatesFactory. How we can retrieve static property? The best way is to retrieve it is via Type instance that represents information about our source type. We can make it an extension method for Type type. This way, it will be more convenient. We need to know about the property we want, so parameter with property name and type parameter with its return type is also a good idea.
Type.StaticPropertyGet<string>("StaticPublicProperty");
Another way is to set source type and property type as type parameters. This way, we will have static method instead of an extension method. Also, it would be less usable since we often do not have type name in compile time (because it is not public or not available before runtime).
DelegateFactory.StaticPropertyGet<TestClass, string>("StaticPublicProperty");
With this assumption we can write console application as test for DelegateFactory.
class ConsoleApplication { private static readonly Type Type = typeof(TestClass); static void Main(string[] args) { var sp = DelegateFactory.StaticPropertyGet<TestClass, string>("StaticPublicProperty"); var sp1 = Type.StaticPropertyGet<string>("StaticPublicProperty"); } }
Now, we can write stub of both methods.
public static class DelegateFactory { public static Func<TProperty> StaticPropertyGet<TSource, TProperty>(string propertyName) { return null; } public static Func<TProperty> StaticPropertyGet<TProperty>(this Type source, string propertyName) { return null; } }
Since the second method (extension one) is more general, we will implement it first. To create a delegate, we need PropertyInfo for the requested property. Yes, we need reflection for that. The thing is, that many of the delegates require reflection, but it is just one time, for creation of delegate. After that, we can use this delegate with just a small overhead, since the program will have to execute two nested methods instead of one.
The most important part in retrieving property via reflection is that we have access to PropertyInfo.GetMethod that way and GetMethod is MethodInfo type, which will have CreateDelegate member. The simplest code for creating delegate for retrieving static public property looks like this.
public static Func<TProperty> StaticPropertyGet<TProperty>(this Type source, string propertyName) { var propertyInfo = source.GetProperty(propertyName, BindingFlags.Static); return (Func<TProperty>)propertyInfo.GetMethod.CreateDelegate(typeof(Func<TProperty>)); }
Looks really simple, which is why this is the first example. CreateDelegate method requires type of delegate to create. For property retrieving, it is Func with just one parameter, which is type of property, so created delegate will not have any parameters. Just simple parameterless method that returns value of static property.
The next thing is to handle non-public (internal, protected and private) properties. We can do it by changing set BindingFlags in GetProperty method. For private and protected members, we just need one more BindingFlags.NonPublic. For internals, this is a little more complicated (and in my opinion, more confusing) and requires both flags Public and NonPublic. An easy way to understand that is to think of internals as kind-of public, but not completely :).
public static Func<TProperty> StaticPropertyGet<TProperty>(this Type source, string propertyName) { var propertyInfo = source.GetProperty(propertyName, BindingFlags.Static); if (propertyInfo == null) { propertyInfo = source.GetProperty(propertyName, BindingFlags.Static | BindingFlags.NonPublic); } if (propertyInfo == null) { propertyInfo = source.GetProperty(propertyName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); } return (Func<TProperty>)propertyInfo.GetMethod.CreateDelegate(typeof(Func<TProperty>)); }
The above code is pretty easy to understand. If property is not public it tries to find it as private or protected. If this is also not the case, it tries internal. Now, we can write method body for overload with type parameters.
public static Func<TProperty> StaticPropertyGet<TSource, TProperty>(string propertyName) { return typeof(TSource).StaticPropertyGet<TProperty>(propertyName); }
Now we can test both methods. We should create delegates for all static properties of TestClass in console application and use them to retrieve properties values, to test if everything works.
class ConsoleApplication { private static readonly Type Type = typeof(TestClass); static void Main(string[] args) { var sp = DelegateFactory.StaticPropertyGet<TestClass, string>("StaticPublicProperty"); var sp1 = Type.StaticPropertyGet<string>("StaticPublicProperty"); var sp2 = Type.StaticPropertyGet<string>("StaticInternalProperty"); var sp3 = Type.StaticPropertyGet<string>("StaticProtectedProperty"); var sp4 = Type.StaticPropertyGet<string>("StaticPrivateProperty"); Console.WriteLine("Static public property value: {0}",sp1()); Console.WriteLine("Static internal property value: {0}", sp2()); Console.WriteLine("Static protected property value: {0}", sp3()); Console.WriteLine("Static private property value: {0}", sp4()); } }
After running this example, we will see the result in console.
Static public property value: StaticPublicProperty Static internal property value: StaticInternalProperty Static protected property value: StaticProtectedProperty Static private property value: StaticPrivateProperty
Static properties works just fine :).
We can check performance of those delegates a little. First, we need to add two new fields to console application type.
private static Stopwatch _stopWatch; private static double _delay = 1e8;
Second is to write loops for tests.
_stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { var test = TestClass.StaticPublicProperty; } _stopWatch.Stop(); Console.WriteLine("Static Public property: {0}", _stopWatch.ElapsedMilliseconds); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { var test = sp1(); } _stopWatch.Stop(); Console.WriteLine("Static Public property retriever: {0}", _stopWatch.ElapsedMilliseconds);
First for loop retrieves static property in an ordinary way. The second one uses delegates. After running the changed code, we can find out that the difference is not that big, really.
Static Public property: 404 Static Public property retriever: 472
Retrieving property value one hundred million times takes around 400ms and using delegate for that is less than 20% slower. Doing more tests, you can check that real performance overhead varies between 15-35%. Of course, it is just a simple string. With more complex properties that are calculating its return value first, the difference would be smaller. Just for the sake of an experiment, we can add one more loop with reflection.
_stopWatch = new Stopwatch(); var pi0 = Type.GetProperty("StaticPublicProperty", BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public).GetMethod; _stopWatch.Start(); for (var i = 0; i < _delay; i++) { var test = pi0.Invoke(null, null); } _stopWatch.Stop(); Console.WriteLine("Static Public property via reflection: {0}", _stopWatch.ElapsedMilliseconds);
After running the changed code, the test results should be similar to this:
Static Public property: 423 Static Public property retriever: 535 Static Public property via reflection: 13261
As you can see, reflection is many times slower. Access to static property is less than half of a second, and doing the same via reflection takes around 13 seconds!
Even by using reflection only one time to create delegate is much more efficient than using it all the time to access that property.
To have a complete look at things, let us do one more test. If you ever used Expression type and its methods, you know that it can be used for similar things as reflection. Certainly, it is possible to create delegate for static property this way.
public static Func<TProperty> StaticPropertyGet2<TProperty>(this Type source, string propertyName) { var te = Expression.Lambda(Expression.Property(null, source, propertyName)); return (Func<TProperty>)te.Compile(); }
This code is much simpler than the one with reflection. It creates a lambda function that could look like this for TestClass.StaticPublicProperty.
() => TestClass.StaticPublicProperty
Now, we can test the performance of creation of delegates by using reflection and expression.
_stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < 10000; i++) { Type.StaticPropertyGet<string>("StaticPublicProperty"); } _stopWatch.Stop(); Console.WriteLine("Static public field creator via reflection: {0}", _stopWatch.ElapsedMilliseconds); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < 10000; i++) { Type.StaticPropertyGet2<string>("StaticPublicProperty"); } _stopWatch.Stop(); Console.WriteLine("Static public field creator via expression: {0}", _stopWatch.ElapsedMilliseconds);
After running this example, we will get results for both loops.
Static public field creator via reflection: 23 Static public field creator via expression: 543
As we can see, the expression is much, much more slower than reflection. I do not investigate this further, but I think that it has something to do with compiling expression to delegate. Unfortunately, it is not possible to create delegates for all members just by using reflection, so it is a necessity to use expressions for some of them, but we will try to not to if this is possible.
There is still the case when we do not know the type of property. What to do then? Since both of StaticPropertyGet require an actual property type, we cannot use them. In that case, we need an overload without this type, but what to use instead? What type should delegate return? There is only one option: object. All types in .NET can be casted to object.
Func<object> spg = Type.StaticPropertyGet("StaticPublicProperty");
Returned delegate is supposed to be a method that does not take parameters and returns value of type object. How to implement this new StaticPropertyGet? We need code for retrieving PropertyInfo for needed static property. The best thing is to create a new method in DelegateFactory using code from StaticPropertyGet<TProperty> extension method.
private static PropertyInfo GetStaticPropertyInfo(Type source, string propertyName) { var propertyInfo = (source.GetProperty(propertyName, BindingFlags.Static) ?? source.GetProperty(propertyName, BindingFlags.Static | BindingFlags.NonPublic)) ?? source.GetProperty(propertyName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); return propertyInfo; }
The main problem is that sometimes we want to retrieve or set property of a value that is of unknown type at compile time. For that, we need a method that creates delegates that retrieves object type value instead of property type value. We can do it by using expressions since we cannot use just CreateDelegate method (it requires compatible delegate signature). The core of this mechanism is Expression.Lambda method, that returns LambdaExpression type object, and LambdaExpression.Compile method that returns Delegate type. With those two, we can create pretty much any code that involves accessing of types members.
public static Func<object> StaticPropertyGet(this Type source, string propertyName) { var propertyInfo = GetStaticPropertyInfo(source, propertyName); return (Func<object>)Expression.Lambda(Expression.Call(propertyInfo.GetMethod)).Compile(); }
Expression.Lambda creates lambda with correct method signature. Expression.Call is used to mark in body of a lambda that call to a property getter should be made – after all, property getter is really get_{property name} method.
We can test if this works correctly with the following code.
var spg = Type.StaticPropertyGet("StaticPublicProperty"); Console.WriteLine(spg());
The above lines executed will write value of ‘StaticPublicProperty’, which is the value of property with this name.
Set accessors
Now when we have a way to get values of static properties, we can write methods for retrieving delegates for set accessors. The mechanism is very similar to get accessors, but instead of Func<TProperty>, Action<TProperty> is used. Of course PropertyInfo.SetMethod creates delegate instead of GetMethod.
public static Action<TProperty> StaticPropertySet<TSource, TProperty>(string propertyName) { return typeof(TSource).StaticPropertySet<TProperty>(propertyName); }
public static Action StaticPropertySet(this Type source, string propertyName) { var propertyInfo = GetStaticPropertyInfo(source, propertyName); return (Action)propertyInfo.SetMethod.CreateDelegate(typeof(Action)); }
As before, for get accessors, there are two methods that use only reflection: one extension method and one with type parameter as source type.
For overload that does not require property type to be known in advance, we have to use expressions as before with StaticPropertyGet.
public static Action<object> StaticPropertySet(this Type source, string propertyName) { var propertyInfo = GetStaticPropertyInfo(source, propertyName); var valueParam = Expression.Parameter(typeof(object)); var convertedValue = Expression.Convert(valueParam, propertyInfo.PropertyType); return (Action<object>)Expression.Lambda(Expression.Call(propertyInfo.SetMethod, convertedValue), valueParam).Compile(); }
This expression is a bit more complicated. Since it should return method that returns void and have one parameter, we need to add this parameter to Lambda call – hence valueParam variable. If you are wondering why it cannot be embedded into Expression.Lambda, the same parameter expression has to be passed to Convert and Lambda methods – expressions are compared by reference. Variable valueParam has to be converted to correct type before passing to property setter method. If not, expression would be valid and would fail to create. After conversion, new expression can be safely passed as parameter to Call method. Not converted expression in valueParam variable is passed as parameter to Lambda call, to mark, that created lambda will have single parameter with type of object.
New methods are called exactly in the same way.
var sps = DelegateFactory.StaticPropertySet<TestClass, string>("StaticPublicProperty"); var sps1 = Type.StaticPropertySet<string>("StaticPublicProperty"); var sps2 = Type.StaticPropertySet<string>("StaticInternalProperty"); var sps3 = Type.StaticPropertySet<string>("StaticProtectedProperty"); var sps4 = Type.StaticPropertySet<string>("StaticPrivateProperty"); var spg5 = Type.StaticPropertyGet("StaticPublicProperty");
In console application, we can create test loops like below.
_stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { TestInstance.PublicProperty = "ordinal way"; } _stopWatch.Stop(); Console.WriteLine("Public set property: {0}", _stopWatch.ElapsedMilliseconds); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { sps1("test"); } _stopWatch.Stop(); Console.WriteLine("Public set property retriever: {0}", _stopWatch.ElapsedMilliseconds); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { sps2("test"); } _stopWatch.Stop(); Console.WriteLine("Internal set property retriever: {0}", _stopWatch.ElapsedMilliseconds); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { sps3("test"); } _stopWatch.Stop(); Console.WriteLine("Protected set property retriever: {0}", _stopWatch.ElapsedMilliseconds); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { sps4("test"); } _stopWatch.Stop(); Console.WriteLine("Private set property retriever: {0}", _stopWatch.ElapsedMilliseconds); Console.WriteLine("Static public property value is {0}", sp1()); Console.WriteLine("Static internal property value is {0}", sp2()); Console.WriteLine("Static protected property value is {0}", sp3()); Console.WriteLine("Static private property value is {0}", sp4());
After running the above code, we will see result with values similar to the ones below.
Public set property: 483 Public set property retriever: 542 Internal set property retriever: 496 Protected set property retriever: 497 Private set property retriever: 542 Static public property value is test Static internal property value is test Static protected property value is test Static private property value is test
As you can see, accessing setters this way is just a little slower, similarly to accessing getters by delegates.
Improvements
What can be done better? Certainly, the code above is not very safe since there are at least two points when it can throw null reference exception. First, when property was not found and propertyInfo variable is null and second when get or set accessor is not available (not every property has both, right?). Both cases are easy to fix.
We should write properties to test those cases first.
public static string StaticOnlyGetProperty => _staticOnlyGetOrSetPropertyValue; public static string StaticOnlySetProperty { set { _staticOnlyGetOrSetPropertyValue = value; } } private static string _staticOnlyGetOrSetPropertyValue = "StaticOnlyGetOrSetPropertyValue";
Now we can fix both methods.
public static Func<TProperty> StaticPropertyGet<TProperty>(this Type source, string propertyName) { var propertyInfo = GetStaticPropertyInfo(source, propertyName); return (Func<TProperty>)propertyInfo?.GetMethod?.CreateDelegate(typeof(Func<TProperty>)); } public static Action<TProperty> StaticPropertySet<TProperty>(this Type source, string propertyName) { var propertyInfo = GetStaticPropertyInfo(source, propertyName); return (Action<TProperty>)propertyInfo?.SetMethod?.CreateDelegate(typeof(Action<TProperty>)); }
As you can see, simple null check in both methods is enough, but we should test it anyway. Following calls in console application will suffice.
All calls return null, which is good. We wanted null instead of error.
Properties
With static properties covered, we can now switch focus to instance properties. Delegates for those are different only by one parameter. Since instance properties need instance to get values of those properties from, delegates will need parameters with those instances. So instead of Func<TProperty>, we will use Func<TSource,TProperty> delegates. Rest of the code is almost the same. As for static properties, we have three methods for each accessor. First, static method with source type parameter and property type parameter, extension method with type parameter the same as property type and method without type parameters. But first we need a method similar to GetStaticPropertyInfo.
private static PropertyInfo GetPropertyInfo(Type source, string propertyName) { var propertyInfo = source.GetProperty(propertyName) ?? source.GetProperty(propertyName, BindingFlags.NonPublic) ?? source.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); return propertyInfo; }
As you can see, it is very similar too
We will start with the first type of method for each accessor.
public static Func<TSource, TProperty> PropertyGet<TSource, TProperty>(string propertyName) { var source = typeof(TSource); var propertyInfo = GetPropertyInfo(source, propertyName); return (Func<TSource, TProperty>)propertyInfo?.GetMethod?.CreateDelegate(typeof(Func<TSource, TProperty>)); } public static Action<TSource, TProperty> PropertySet<TSource, TProperty>(string propertyName) { var source = typeof(TSource); var propertyInfo = GetPropertyInfo(source, propertyName); return (Action<TSource, TProperty>)propertyInfo?.SetMethod?.CreateDelegate(typeof(Action<TSource, TProperty>)); }
As you can see, there is null propagation operator (?.) used for null check of property info and needed accessor method.
Ok. To test it, we need to create instance properties in TestClass in console application.
public class TestClass { public TestClass() { PublicProperty = "PublicProperty"; InternalProperty = "InternalProperty"; ProtectedProperty = "ProtectedProperty"; PrivateProperty = "PrivateProperty"; } public string PublicProperty { get; set; } internal string InternalProperty { get; set; } protected string ProtectedProperty { get; set; } private string PrivateProperty { get; set; } }
And static readonly instance of TestClass type in console application to use in all tests that involves instance.
private static readonly TestClass TestInstance = new TestClass();
Test code looks like below.
var ps1 = DelegateFactory.PropertySet<TestClass, string>("PublicProperty"); var ps2 = DelegateFactory.PropertySet<TestClass, string>("InternalProperty"); var ps3 = DelegateFactory.PropertySet<TestClass, string>("ProtectedProperty"); var ps4 = DelegateFactory.PropertySet<TestClass, string>("PrivateProperty"); Console.WriteLine("Public property value is {0}", pg1(TestInstance)); Console.WriteLine("Internal property value is {0}", pg2(TestInstance)); Console.WriteLine("Protected property value is {0}", pg3(TestInstance)); Console.WriteLine("Private property value is {0}", pg4(TestInstance)); ps1(TestInstance, "test"); ps2(TestInstance, "test"); ps3(TestInstance, "test"); ps4(TestInstance, "test"); Console.WriteLine("Public property value is {0}", pg1(TestInstance)); Console.WriteLine("Internal property value is {0}", pg2(TestInstance)); Console.WriteLine("Protected property value is {0}", pg3(TestInstance)); Console.WriteLine("Private property value is {0}", pg4(TestInstance));
After running this test code, we will see console output like this.
Public property value is PublicProperty Internal property value is InternalProperty Protected property value is ProtectedProperty Private property value is PrivateProperty Public property value is test Internal property value is test Protected property value is test Private property value is test
First four lines are values returned by get delegates and are consistent with default values of TestClass instance. The following four lines are after calls to set delegates and have new ‘test’ values. Good. Everything works!
Improvements
The main problem with methods like the above for retrieving and setting instance properties is that those require type to be known at compile time, which is sometimes impossible since some types are private. But if you do not have type, you cannot create delegate that has this type instance as parameter. The only solution for this is to create a method that takes object and hopes that it is the correct instance of correct type :). The problem with that solution is that you cannot pass parameter of type of object to property getter as source instance. You first have to cast it to the appropriate type. So instead of just one proxy method, you have cast to appropriate type too inside this proxy method, that retrieves or sets property. This is the point when we have to use expressions as in static properties.
The second problem is the same as with static properties. You do not necessarily have to know property type at compilation time. It may be either unavailable or private.
First of all, our new overloads would look like this:
Type.PropertyGet<string>("PublicProperty"); Type.PropertyGet("PublicProperty");
Pretty simple. In the first case we know type of property and in the second one – we do not. First would return string and second one the same string but as object type.
We need two new overloads for PropertyGet method then. Let us start with the one with known property type since it will be only a bit, but a little simpler nevertheless.
With property info returned from GetPropertyInfo method, we can create the appropriate delegate with Expression class. The code for accessing property getter by its instance as object looks like this.
var sourceObjectParam = Expression.Parameter(typeof(object)); var @delegate = (Func<object, object>)Expression.Lambda(Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod), sourceObjectParam).Compile();
The mechanism is simple. First we need to create ParameterExpression with object type. It is necessary because it is referenced twice: in Expression.Lambda method as target lambda parameter and in Expression.Convert method as a target of a conversion. After that we create lambda expression (Expression.Lambda method) with body with method call (Expression.Call method) to property getter with parameter of the call from result of conversion (Expression.Convert method) of lambda object parameter. It may look complicated (even more since Expression API is in my opinion a little chaotic; notice that Lambda method takes first body of a method, then parameters, but Call method first takes subject of a call and then method to call), but in reality it is not. After compilation, the expression will be roughly equivalent to lambda like below.
Func<object,string> @delegate = o => ((TestClass)o).PublicProperty;
Looks much clearer, right? 🙂
With this knowledge, we can create the first method.
public static Func<object, TProperty> PropertyGet<TProperty>(this Type source, string propertyName) { var propertyInfo = GetPropertyInfo(source, propertyName); var sourceObjectParam = Expression.Parameter(typeof(object)); return (Func<object, TProperty>)Expression.Lambda(Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod), sourceObjectParam).Compile(); }
In the same way we can create second method, but with different declaration of return type. It is very important to mark that even if method with return type object, written in C# can work without cast to object in return statement (because any type can be implicitly casted to object without extra casting), this do not work in expressions. As far as I can tell from my tests it works only for reference type. For value types such as int, DateTime etc. we need conversion to object. PropertyInfo class have IsClass property that allows to check if conversion is needed or not.
public static Func<object, object> PropertyGet(this Type source, string propertyName) { var propertyInfo = GetPropertyInfo(source, propertyName); var sourceObjectParam = Expression.Parameter(typeof(object)); Expression returnExpression = Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod); if (!propertyInfo.PropertyType.IsClass) { returnExpression = Expression.Convert(returnExpression, typeof(object)); } return (Func<object, object>)Expression.Lambda(returnExpression, sourceObjectParam).Compile(); }
As you can see, the second method is more generic and almost the same as the first. Because of that, we can safely rewrite the first method like below.
public static Func<object, TProperty> PropertyGet<TProperty>(this Type source, string propertyName) { return source.PropertyGet(propertyName) as Func<object, TProperty>; }
After adding new property to TestClass, with int type, PublicPropertyInt and following lines to console application, we can test if those methods work.
var pgo1 = Type.PropertyGet<string>("PublicProperty"); var pgo2 = Type.PropertyGet("PublicProperty"); var pgo3 = Type.PropertyGet("PublicPropertyInt"); Console.WriteLine("Public property by object and property type {0}", pgo1(TestInstance)); Console.WriteLine("Public property by objects {0}", pgo2(TestInstance)); Console.WriteLine("Public property by objects and with return value type {0}", pgo3(TestInstance));
It will result in the following lines in console output.
Public property by object and property type PublicProperty Public property by objects PublicProperty Public property by objects and with return value type 0
Now, we can write similar methods for setters. The core difference is that correct delegate will be Action instead of Func so instead of one parameter, it will have two: instance and value to set. In method with known parameter type, we only have change delegate but in the one without type parameters, we have to convert instance and value. We will start with the second one. First, we have to create ExpressionParameter for new property value.
var propertyValueParam = Expression.Parameter(propertyInfo.PropertyType);
With this, we can change the returning value of lambda (if we are changing similar PropertyGet method).
return (Action<object, object>)Expression.Lambda(Expression.Call( Expression.Convert(sourceObjectParam, source), propertyInfo.SetMethod, Expression.Convert(propertyValueParam, propertyInfo.PropertyType)), sourceObjectParam, propertyValueParam).Compile();
As you can see, it creates Action delegate with two parameters – sourceObjectParam and propertyValueParam – both are converted to correct type first. The above lines could be converted to the following lambda.
Action<object, object> @delegate = (i, v)=>((TestClass)i).PublicProperty = (string)v;
PropertySet overload with known property type is more specific than the above method, but at the same time, it will have knowledge of both types: value parameter and property type. This is why we can use its code for both overloads. Just if property type is correct, there is no need for conversion.
Final version of both looks like this.
public static Action<object, TProperty> PropertySet<TProperty>(this Type source, string propertyName) { var propertyInfo = GetPropertyInfo(source, propertyName); var sourceObjectParam = Expression.Parameter(typeof(object)); ParameterExpression propertyValueParam; Expression valueExpression; if (propertyInfo.PropertyType == typeof(TProperty)) { propertyValueParam = Expression.Parameter(propertyInfo.PropertyType); valueExpression = propertyValueParam; } else { propertyValueParam = Expression.Parameter(typeof(TProperty)); valueExpression = Expression.Convert(propertyValueParam, propertyInfo.PropertyType); } return (Action<object, TProperty>)Expression.Lambda(Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.SetMethod, valueExpression), sourceObjectParam, propertyValueParam).Compile(); } public static Action<object, object> PropertySet(this Type source, string propertyName) { return source.PropertySet<object>(propertyName); }
The last thing that should be fixed is null checking for GetMethod and SetMethod – after all it is already done in previous examples. This requires single if in PropertySet and PropertyGet methods.
var propertyInfo = GetPropertyInfo(source, propertyName); if (propertyInfo?.GetMethod == null) { return null; } var propertyInfo = GetPropertyInfo(source, propertyName); if (propertyInfo?.SetMethod == null) { return null; }
If either property or accessor does not exist (after all properties do not always have both set and get accessor) method will return null instead of delegate.
Indexers
Indexers are just special type of instance properties. The best way to think about them is as special kind of get and set accessors, with extra index parameters. And they actually are special properties with name “Item”. We should start with creating indexer in our test class.
private readonly List<int> _indexerBackend = new List<int>(10); public int this[int i] { get { return _indexerBackend[i]; } set { _indexerBackend[i] = value; } }
Yes there is no index checking, nor automatic collection resizing, etc. It is just test code so it does not need to be safe.
This is really easy case and delegate for this will be also everything but complex. We need Func<TestInstance,int,int> for this indexer. The simplest way to create it with a single call will be like below.
var ig1 = DelegateFactory.IndexerGet<TestClass, int, int>();
How to implement IndexerGet method? The most important is to get PropertyInfo object for indexer.
private const string Item = "Item"; private static PropertyInfo GetIndexerPropertyInfo(Type source, Type returnType, Type[] indexesTypes) { var propertyInfo = (source.GetProperty(Item, returnType, indexesTypes) ?? source.GetProperty(Item, BindingFlags.NonPublic, null, returnType, indexesTypes, null)) ?? source.GetProperty(Item, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, returnType, indexesTypes, null); return propertyInfo; }
For public indexer, the way to obtain PropertyInfo is almost the same as for property – of course, there is a need for two new parameters that indicate return and indexes type of desired indexer. For non-public indexer, things get a little more complicated since we have to use more general overload. Of course, all of calls to GetProperty method use constant value “Item” as name of property. With property info, we can create delegate for indexer with single index parameter very similar to delegate for ordinary property.
public static Func<TSource, TIndex, TReturn> IndexerGet<TSource, TReturn, TIndex>() { var propertyInfo = GetIndexerPropertyInfo(typeof(TSource), typeof(TReturn), new[] { typeof(TIndex) }); return (Func<TSource, TIndex, TReturn>) propertyInfo?.GetMethod?.CreateDelegate(typeof(Func<TSource, TIndex, TReturn>)); }
As you can see, the main difference is that instead of just one parameter with instance (like in properties) there is one more extra with index parameter. Besides that it is pretty much the same.
Ok, but what if indexer has more than one index? How about two or three? It is possible too and since IndexerGet method takes type parameters, we cannot make a more universal method that returns different delegates for 2, 3, 4 or more parameters. For each of those types of indexers, there is a necessity for other methods with different set of types parameters. Since indexers are rarely used and mostly for indexing operations of some kind, we will create methods for only up to 3 index parameters (for up to 3D arrays).
public static Func<TSource, TIndex, TIndex2, TReturn> IndexerGet<TSource, TReturn, TIndex, TIndex2>() { var propertyInfo = GetIndexerPropertyInfo(typeof(TSource), typeof(TReturn), new[] { typeof(TIndex), typeof(TIndex2) }); return (Func<TSource, TIndex, TIndex2, TReturn>) propertyInfo?.GetMethod?.CreateDelegate(typeof(Func<TSource, TIndex, TIndex2, TReturn>)); } public static Func<TSource, TIndex, TIndex2, TIndex2, TReturn> IndexerGet<TSource, TReturn, TIndex, TIndex2, TIndex3>() { var propertyInfo = GetIndexerPropertyInfo(typeof(TSource), typeof(TReturn), new[] { typeof(TIndex), typeof(TIndex2), typeof(TIndex3) }); return (Func<TSource, TIndex, TIndex2, TIndex2, TReturn>) propertyInfo?.GetMethod?.CreateDelegate(typeof(Func<TSource, TIndex, TIndex2, TIndex2, TReturn>)); }
The above method will spawn delegates for two and three indexes and are called very similar to 1D array indexer overload. There is of course possibility of writing methods that return delegates for more indexes, up to 16 – it is the limit of Func class.
var ig1 = DelegateFactory.IndexerGet<TestClass, int, int, int>(); var ig2 = DelegateFactory.IndexerGet<TestClass, int, int, int, int>();
As you probably already noticed, all of above methods for indexers getters can only create delegates only if we know the type of class. If we do not (because it is private or do not know it in compile-time), we need a method that takes Type parameter instead. We can use the same extension method convention as before with properties.
var ig1 = Type.IndexerGet<int, int>(); var ig2 = Type.IndexerGet<int, int, int>(); var ig3 = Type.IndexerGet<int, int, int, int>();
The same problem occurs with indexers as with properties before. Since we do pass instance and not the type itself, we can’t create delegates with known type of parameter – it needs to be object. Because of that, we again need expressions. We will start with indexer with single index parameter.
public static Func<object, TIndex, TReturn> IndexerGet<TReturn, TIndex>(this Type source) { var indexType = typeof(TIndex); var returnType = typeof(TReturn); var propertyInfo = GetIndexerPropertyInfo(source, returnType, new[] { indexType }); var sourceObjectParam = Expression.Parameter(typeof(object)); var paramExpression = Expression.Parameter(indexType); return (Func<object, TIndex, TReturn>)Expression.Lambda( Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod, paramExpression), sourceObjectParam, paramExpression).Compile(); }
It is very similar to PropertyGet method that creates delegate that takes object as source instance. The difference is again with index parameter. Because it is an object, there is a need for conversion first. After creating lambda expression that takes instance object and index parameters, it is then compiled and returned as delegate. It can be represented as the following lambda statement.
Func<object, int, int> @delegate = (o, i) => ((TestClass)o)[i];
Ok. How about two or three indexes? Luckily, Expression.Lambda and Expression.Call methods, have overloads that take an unspecified number of parameters. With that, we can easily rewrite most of the code for creating delegate, to more a universal version. We should write a method that creates Delegate instance by compiling expression from indexer return type and indexes types.
public static Delegate DelegateIndexerGet(Type source, Type returnType, params Type[] indexTypes) { var propertyInfo = GetIndexerPropertyInfo(source, returnType, indexTypes); var sourceObjectParam = Expression.Parameter(typeof(object)); var paramsExpression = new ParameterExpression[indexTypes.Length]; for (var i = 0; i < indexTypes.Length; i++) { var indexType = indexTypes[i]; paramsExpression[i] = Expression.Parameter(indexType); } return Expression.Lambda( Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod, paramsExpression), new[] { sourceObjectParam }.Concat(paramsExpression)).Compile(); }
The most important change is that this method takes an unspecified number of indexes types. The second change is that instead of known number of parameters in Lambda and Call we have collection of parameters (with one more in lambda – source instance parameter). Aside of that, it is the same code as before and with this new method, we can implement tree extension methods above.
public static Func<object, TIndex, TReturn> IndexerGet<TReturn, TIndex>(this Type source) { var indexType = typeof(TIndex); return (Func<object, TIndex, TReturn>)DelegateIndexerGet(source, typeof(TReturn), indexType); } public static Func<object, TIndex, TIndex2, TReturn> IndexerGet<TReturn, TIndex, TIndex2>(this Type source) { var indexType = typeof(TIndex); var indexType2 = typeof(TIndex2); return (Func<object, TIndex, TIndex2, TReturn>)DelegateIndexerGet(source, typeof(TReturn), indexType, indexType2); } public static Func<object, TIndex, TIndex2, TIndex3, TReturn> IndexerGet<TReturn, TIndex, TIndex2, TIndex3>(this Type source) { var indexType = typeof(TIndex); var indexType2 = typeof(TIndex2); var indexType3 = typeof(TIndex3); return (Func<object, TIndex, TIndex2, TIndex3, TReturn>)DelegateIndexerGet(source, typeof(TReturn), indexType, indexType2, indexType3); }
It is straightforward code. Type parameters are changed to instances of Type class and passed to DelegateIndexerGet method. Returned value is casted to expected Func class. But those new methods still lacks some features though. Indexers are not required to have primitive index types – they can be anything. Even if this is rarely the case, we have to have a way to create delegates for more indexing parameters. But much more important is return type of indexer – it can any type and it is more common. We need a more generic method then. For starters, we can create method that creates delegate Func<object,object,object> for indexers that have only one parameter.
public static Func<object, object, object> IndexerGet(this Type source, Type returnType, Type indexType) { var propertyInfo = GetIndexerPropertyInfo(source, returnType, new[] { indexType }); var sourceObjectParam = Expression.Parameter(typeof(object)); var indexObjectParam = Expression.Parameter(typeof(object)); Expression returnExpression = Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod, Expression.Convert(indexObjectParam, indexType)); if (!propertyInfo.PropertyType.IsClass) { returnExpression = Expression.Convert(returnExpression, typeof(object)); } return (Func<object, object, object>) Expression.Lambda(returnExpression, sourceObjectParam, indexObjectParam).Compile(); }
It is almost the same as PropertyGet overload that works on objects instead of concrete types – it just takes one parameter more.
Ideally, there would have to be a way to get all of indexer types in a uniform way. It is possible, for example, to write a delegate that takes two parameters: instance and array of indexes and returns object.
var ig = Type.IndexerGet(typeof(int), typeof(int), typeof(int), typeof(int)); var t = ig(TestInstance, new object[] { 0, 0, 0 });
The problem with this method is that it requires much more calculation which involves reading array of indexes, converting them to correct types, calling indexer getter and converting its value to object. Because of all of that, it will be much slower than just call to getter, but if we do not have the luxury of more strict delegate (like Func<TSource, TIndex, TReturn>), it is the best we can do.
public static Func<object, object[], object> IndexerGet(this Type source, Type returnType, params Type[] indexTypes) { var propertyInfo = GetIndexerPropertyInfo(source, returnType, indexTypes); var sourceObjectParam = Expression.Parameter(typeof(object)); var indexesParam = Expression.Parameter(typeof(object[])); var paramsExpression = new Expression[indexTypes.Length]; for (var i = 0; i < indexTypes.Length; i++) { var indexType = indexTypes[i]; paramsExpression[i] = Expression.Convert(Expression.ArrayIndex(indexesParam, Expression.Constant(i)), indexType); } Expression returnExpression = Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod, paramsExpression); if (!propertyInfo.PropertyType.IsClass) { returnExpression = Expression.Convert(returnExpression, typeof(object)); } return (Func<object, object[], object>)Expression.Lambda( returnExpression, sourceObjectParam, indexesParam).Compile(); }
The main change from the previous methods is that we have a single parameter with all indexes passed to lambda in ParameterExpression.
var indexesParam = Expression.Parameter(typeof(object[]));
Since this is an array of indexes, we need to get every single one of them from it. It can be done by Expression.ArrayIndex method. After retrieving index value, we need to cast it to index type at the same position. With collection of converted indexes, we can pass it to Expression.Call as a collection of indexer getter parameters. There is similar change (if we comparing code with PropertyGet method without type parameters), that returns object instead of property type instance: if return type is value type (int, byte etc.) conversion to object is done. Here is the translation to more common lambda statement.
Func<object, object[], object> @delegate = (o, i) => (object)((TestClass)o)[(int)i[0], (int)i[1], (int)i[3]];
Yes, of course there is no checking if indexes or instance is of correct type. There is no checking if array of indexes have correct size nor check for index types. There are plenty of things that may go wrong. But we do write delegates for speed, not for safety. And if we are talking about performance, it is about time to test it. We should add few indexers to TestClass first.
public int this[int i1, int i2, int i3] { get { return i1; } set { } } internal string this[string s] => s; private long this[long s] => s; private int this[int i1, int i2] { get { return i1; } set { } }
Test code for the above indexers and the first one looks like this.
_stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { var test = TestInstance[0]; } _stopWatch.Stop(); Console.WriteLine("Public indexer: {0}", _stopWatch.ElapsedMilliseconds); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { var test = ig1(TestInstance, 0); } _stopWatch.Stop(); Console.WriteLine("Public indexer retriever: {0}", _stopWatch.ElapsedMilliseconds); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { var test = TestInstance[0, 0, 0]; } _stopWatch.Stop(); Console.WriteLine("Multiple index indexer directly: {0}", _stopWatch.ElapsedMilliseconds); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { var test = ig10(TestInstance, new object[] { 0, 0, 0 }); } _stopWatch.Stop(); Console.WriteLine("Multiple index indexer via delegate with array: {0}", _stopWatch.ElapsedMilliseconds); var indexerInfo = Type.GetProperty("Item", typeof(int), new Type[] { typeof(int), typeof(int), typeof(int) }); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { var test = indexerInfo.GetValue(TestInstance, new object[] { 0, 0, 0 }); } _stopWatch.Stop(); Console.WriteLine("Multiple index indexer via reflection: {0}", _stopWatch.ElapsedMilliseconds);
Running this code will result in console output as below.
Public indexer: 1555 Public indexer retriever: 1603 Multiple index indexer directly: 525 Multiple index indexer via delegate with array: 5847 Multiple index indexer via reflection: 44755
As you can see, calling indexer by proxy delegate is not that much slower. Of course, for more complex indexers than calling indexing operation on backend field, like in first indexer in TestClass, this difference will be even less visible. Another thing worth mentioning is that calling delegate with array instead of set of parameters make it much slower than the original indexer. In test indexer, it is about ten times slower. The difference is that big because indexer itself is just a single return operation when delegate will have to index and convert all of parameters and pass it to indexer. Difference would probably be even greater for indexers with more than three parameters (another thing is why write such indexer then instead of single method). On the other hand, if indexer would be more complex, the difference would be less visible. Anyway, still delegate is few times quicker than reflection call.
Improvements
In some cases, there might be a small problem. Indexer name does not necessarily have to be an ‘Item’. According to the documentation, it can be changed like this.
[System.Runtime.CompilerServices.IndexerName("TheItem")] internal string this[string s] => s;
Has anybody ever used that? 🙂 I do not think that it is very common, but it does not matter. We can make it work anyway. We could write new overloads of our methods with indexer name parameter, but it would require too much work and since we are smart and lazy, there is a better way :). There is the possibility of renaming indexer property, but you have to do it for all indexers at once in the same class. GetIndexerPropertyInfo method already looks for indexer info few times. Why do not force it to do that few more times? If ‘Item’ is not there, we can search for other properties that are indexers.
private static PropertyInfo GetIndexerPropertyInfo(Type source, Type returnType, Type[] indexesTypes, string indexerName = null) { indexerName = indexerName ?? Item; var propertyInfo = (source.GetProperty(indexerName, returnType, indexesTypes) ?? source.GetProperty(indexerName, BindingFlags.NonPublic, null, returnType, indexesTypes, null)) ?? source.GetProperty(indexerName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, returnType, indexesTypes, null); if (propertyInfo != null) { return propertyInfo; } var indexer = source.GetProperties().FirstOrDefault(p => p.GetIndexParameters().Length > 0); return indexer != null ? GetIndexerPropertyInfo(source, returnType, indexesTypes, indexer.Name) : null; }
Indexer properties returned from reflection do not have null array returned from method GetIndexParameters. This way, we can find name for all indexers and then do more specific search for the one we really looking for. And end user of DelegatesFactory does not have to think about the name of an indexer at all (which is good since indexers do not have names in C# code).
To complete subject about an indexers getters, we need to check for propertyInfo variable and PropertyInfo.Get/SetMethod properties if they are not null. It should be done the same way as with properties.
if (propertyInfo?.GetMethod == null) { return null; }
Setters
Now we can jump to indexers setters. It is a very similar situation like with setters for properties. First, we use Action classes instead of Func, second we have one more parameter for value to set in indexer. That is all. For example, first IndexerGet method after conversion to IndexerSet looks like this.
public static Action<TSource, TIndex, TProperty> IndexerSet<TSource, TIndex, TProperty>() { var sourceType = typeof(TSource); var propertyInfo = GetIndexerPropertyInfo(sourceType, typeof(TProperty), new[] { typeof(TIndex) }); return (Action<TSource, TIndex, TProperty>) propertyInfo?.SetMethod?.CreateDelegate(typeof(Action<TSource, TIndex, TProperty>)); }
Methods for 2D and 3D indexers are also very similar.
public static Action<TSource, TIndex, TIndex2, TReturn> IndexerSet<TSource, TReturn, TIndex, TIndex2>() { var propertyInfo = GetIndexerPropertyInfo(typeof(TSource), typeof(TReturn), new[] { typeof(TIndex), typeof(TIndex2) }); return (Action<TSource, TIndex, TIndex2, TReturn>) propertyInfo?.SetMethod?.CreateDelegate(typeof(Action<TSource, TIndex, TIndex2, TReturn>)); } public static Action<TSource, TIndex, TIndex2, TIndex2, TReturn> IndexerSet<TSource, TReturn, TIndex, TIndex2, TIndex3>() { var propertyInfo = GetIndexerPropertyInfo(typeof(TSource), typeof(TReturn), new[] { typeof(TIndex), typeof(TIndex2), typeof(TIndex3) }); return (Action<TSource, TIndex, TIndex2, TIndex2, TReturn>) propertyInfo?.SetMethod?.CreateDelegate( typeof(Action<TSource, TIndex, TIndex2, TIndex2, TReturn>)); }
With method with array of indexes, it is a little more complicated.
public static Action<object, object[], object> IndexerSet(this Type source, Type returnType, params Type[] indexTypes) { var propertyInfo = GetIndexerPropertyInfo(source, returnType, indexTypes); if (propertyInfo?.SetMethod == null) { return null; } var sourceObjectParam = Expression.Parameter(typeof(object)); var indexesParam = Expression.Parameter(typeof(object[])); var valueParam = Expression.Parameter(typeof(object)); var paramsExpression = new Expression[indexTypes.Length + 1]; for (var i = 0; i < indexTypes.Length; i++) { var indexType = indexTypes[i]; paramsExpression[i] = Expression.Convert(Expression.ArrayIndex(indexesParam, Expression.Constant(i)), indexType); } paramsExpression[indexTypes.Length] = Expression.Convert(valueParam, returnType); Expression returnExpression = Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.SetMethod, paramsExpression); return (Action<object, object[], object>)Expression.Lambda( returnExpression, sourceObjectParam, indexesParam, valueParam).Compile(); }
First difference is of course Action instead of Func. Second is that returnType param is used in Expression.Convert to change type of valueParam parameter, which is the last parameter of Expression.Call.
var valueParam = Expression.Parameter(typeof(object)); paramsExpression[indexTypes.Length] = Expression.Convert(valueParam, returnType)
The same valueParam variable is passed as the last parameter (after array of indexes) of Expression.Lambda method. Everything else is similar to code in PropertyGet, PropertySet and IndexerGet.
With extension methods, changes are in new DelegateIndexerSet method.
public static Delegate DelegateIndexerSet(Type source, Type returnType, params Type[] indexTypes) { var propertyInfo = GetIndexerPropertyInfo(source, returnType, indexTypes); if (propertyInfo?.SetMethod == null) { return null; } var sourceObjectParam = Expression.Parameter(typeof(object)); var valueParam = Expression.Parameter(returnType); var indexExpressions = new ParameterExpression[indexTypes.Length]; for (var i = 0; i < indexTypes.Length; i++) { var indexType = indexTypes[i]; indexExpressions[i] = Expression.Parameter(indexType); } var callArgs = indexExpressions.Concat(new [] { valueParam }).ToArray(); var paramsExpressions = new[] { sourceObjectParam }.Concat(callArgs); return Expression.Lambda( Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.SetMethod, callArgs), paramsExpressions).Compile(); }
Of course, there is SetMethod used instead of getter equivalent. Also, there is new variable valueParam as in the method above. And new collections, callArgs and paramesExpressions, that allows us to pass appropriate values to lamba and setter calls. The first one, is a collection of indexes parameters expressions and value expression. The second one is source instance parameter expression, the same indexes expressions, and value parameter at the end.
And finally, we can write last of IndexerSet methods with new DelegateIndexerSet method.
public static Action<object, TIndex, TReturn> IndexerSet<TReturn, TIndex>(this Type source) { var indexType = typeof(TIndex); return (Action<object, TIndex, TReturn>)DelegateIndexerSet(source, typeof(TReturn), indexType); } public static Action<object, TIndex, TIndex2, TReturn> IndexerSet<TReturn, TIndex, TIndex2>(this Type source) { var indexType = typeof(TIndex); var indexType2 = typeof(TIndex2); return (Action<object, TIndex, TIndex2, TReturn>)DelegateIndexerSet(source, typeof(TReturn), indexType, indexType2); } public static Action<object, TIndex, TIndex2, TIndex3, TReturn> IndexerSet<TReturn, TIndex, TIndex2, TIndex3>(this Type source) { var indexType = typeof(TIndex); var indexType2 = typeof(TIndex2); var indexType3 = typeof(TIndex3); return (Action<object, TIndex, TIndex2, TIndex3, TReturn>)DelegateIndexerSet(source, typeof(TReturn), indexType, indexType2, indexType3); }
Those methods are called exactly the same as the ones for get accessors. They just create different delegates.
var is1 = DelegateFactory.IndexerSet<TestClass, int, int>(); var is2 = DelegateFactory.IndexerSet<TestClass, int, int, int>(); var is3 = DelegateFactory.IndexerSet<TestClass, int, int, int, int>(); var is4 = Type.IndexerSet(typeof(int), typeof(int), typeof(int), typeof(int)); var is5 = Type.IndexerSet<int, int>(); var is6 = Type.IndexerSet<int, int, int>(); var is7 = Type.IndexerSet<int, int, int, int>(); is1(TestInstance, 0, 1); is2(TestInstance, 0, 0, 1); is3(TestInstance, 0, 0, 0, 1); is4(TestInstance, new object[] { 0, 0, 0 }, 1); is5(TestInstance, 1, 2); is6(TestInstance, 0, 0, 2); is7(TestInstance, 0, 0, 0, 2);
Static Fields
We have properties and indexers covered. Now we should try to make delegates for fields. First the static ones. How to do it? With properties (or methods for that matter), we have MethodInfo objects for each accessor, but fields are fields and they do not have accessor, which are really only methods with a special name. Because of that, we cannot create delegates from FieldInfo returned from reflection. We can do that only by creating delegates from expressions. There is Expression.Field exactly for that kind of data access.
But before implementing any of the new methods, it is about time to write some test static fields.
public static string StaticPublicField = "StaticPublicField"; public static string StaticInternalField = "StaticInternalField"; public static string StaticProtectedField = "StaticProtectedField"; public static string StaticPrivateField = "StaticPrivateField";
Get static field value
New methods for creating delegates that will return value of a static field will be called StaticFieldGet. First, we will write overload that takes type as type param – for known types at compile time. They will be called in the following way.
var sfg1 = DelegateFactory.StaticFieldGet<TestClass, string>("StaticPublicField");
Simplest implementation would look like below.
public static Func<TField> StaticFieldGet<TSource, TField>(string fieldName) { var source = typeof(TSource); var lambda = Expression.Lambda(Expression.Field(null, source, fieldName)); return (Func<TField>)lambda.Compile(); }
Much simpler in contrast to some indexer expressions for delegates. In lambda statement the same expression is even more clear.
Func<string> @delegate = () => TestClass.StaticPublicField;
Simple function that returns value. Ok, but what will happen if field name is invalid? Expression will fail and the application will crash. With code like that, we can’t really remedy this in any way beside try-catch statement. Luckily, there is another overload of Expression.Field method that takes FieldInfo object instead of a string with field name. We can search for desired field ourselves. If field is not found and FieldInfo object is null, null value will be returned instead of a delegate from StaticFieldGet method. To retrieve field data, we can create a new method GetFieldInfo based on code from GetPropertyInfo method.
private static FieldInfo GetStaticFieldInfo(Type source, string fieldName) { var fieldInfo = (source.GetField(fieldName, BindingFlags.Static) ?? source.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic)) ?? source.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); return fieldInfo; }
And now, we can write a safer method using field info.
public static Func<TField> StaticFieldGet<TSource, TField>(string fieldName) { var fieldInfo = GetStaticFieldInfo(typeof(TSource), fieldName); if (fieldInfo != null) { var lambda = Expression.Lambda(Expression.Field(null, fieldInfo)); return (Func<TField>)lambda.Compile(); } return null; }
There is null check and another overload of Expression.Field method with field data as parameter. Rest of the code was already covered in different methods.
We can also test both methods for performance difference if pure expression version would be renamed to i.e. StaticFieldGetExpr.
_stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < 10000; i++) { DelegateFactory.StaticFieldGet<TestClass, string>("StaticPublicField"); } _stopWatch.Stop(); Console.WriteLine("Static public field creator via reflection: {0}", _stopWatch.ElapsedMilliseconds); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < 10000; i++) { DelegateFactory.StaticFieldGetExpr<TestClass, string>("StaticPublicField"); } _stopWatch.Stop(); Console.WriteLine("Static public field creator via expression: {0}", _stopWatch.ElapsedMilliseconds);
According to performance test, the difference is either negligible or with small indication of reflection one as faster.
Static public field creator via reflection: 538 Static public field creator via expression: 640
But again, what if we do not have either source type or field type or even both? After all, fields are almost always private and can have private types.
As before, we can fix that by adding new overloads.
var sfg1 = Type.StaticFieldGet<string>("StaticPublicField"); var sfg2 = Type.StaticFieldGet("StaticPublicField");
The same as with properties and indexers, first overload should return field type and second just object.
public static Func<TField> StaticFieldGet<TField>(this Type source, string fieldName) { var fieldInfo = GetStaticFieldInfo(source, fieldName); if (fieldInfo != null) { var lambda = Expression.Lambda(Expression.Field(null, fieldInfo)); return (Func<TField>)lambda.Compile(); } return null; } public static Func<object> StaticFieldGet(this Type source, string fieldName) { var fieldInfo = GetStaticFieldInfo(source, fieldName); if (fieldInfo != null) { Expression returnExpression = Expression.Field(null, fieldInfo); if (!fieldInfo.FieldType.IsClass) { returnExpression = Expression.Convert(returnExpression, typeof(object)); } var lambda = Expression.Lambda(returnExpression); return (Func<object>)lambda.Compile(); } return null; }
If field has value type conversion to object is performed. Besides that, it is almost the same code as in the previous overload. Because of that, the previous method can be simplified like this.
public static Func StaticFieldGet<TSource, TField>(string fieldName) { var source = typeof(TSource); return source.StaticFieldGet(fieldName); }
Now, in the same and easy manner, we can retrieve fields with all visibilities.
var sfg3 = Type.StaticFieldGet<string>("StaticPublicField"); var sfg4 = Type.StaticFieldGet<string>("StaticInternalField"); var sfg5 = Type.StaticFieldGet<string>("StaticProtectedField"); var sfg6 = Type.StaticFieldGet<string>("StaticPrivateField"); Console.WriteLine("Static public field value: {0}", sfg3()); Console.WriteLine("Static internal field value: {0}", sfg4()); Console.WriteLine("Static protected field value: {0}", sfg5()); Console.WriteLine("Static private field value: {0}", sfg6());
After running this in test console application, we will set correct values of all fields.
Static public field value: StaticPublicField Static internal field value: StaticInternalField Static protected field value: StaticProtectedField Static private field value: StaticPrivateField
Set static field value
We now have a way to retrieve values of static fields. What about the possibility of changing them? It can be done and as before with properties and indexers. Most of the code stays the same. Of course, returned delegates would be of Action class. There is still Expression.Field in play, but need to be combined with Expression.Assign to be able to change field value with assignment operation.
For retrieving fields values, we had three methods: static, extension method with type of field and extension method that returns an object. For changing fields, we will also write three overloads.
var sfs1 = DelegateFactory.StaticFieldSet<TestClass, string>("StaticPublicField"); var sfs2 = Type.StaticFieldSet<string>("StaticPublicField"); var sfs3 = Type.StaticFieldSet("StaticPublicField");
Since the first one can be easily rewritten as proxy to the second one, let’s start the implementation with the second one.
public static Action<TField> StaticFieldSet<TField>(this Type source, string fieldName) { var fieldInfo = GetStaticFieldInfo(source, fieldName); if (fieldInfo != null) { var valueParam = Expression.Parameter(typeof(TField)); var lambda = Expression.Lambda(typeof(Action<TField>), Expression.Assign(Expression.Field(null, fieldInfo), valueParam), valueParam); return (Action<TField>)lambda.Compile(); } return null; }
As you can see, most of a change is in the expression itself since there is not a single call to Expression.Field, but it is nested in Expression.Assign to mark that lambda body assign value parameter to field. Of course, produced delegate is Action not a Func. For TestClass.StaticPublicField field, it would produce lambda like below.
Action<string> @delegate = v => TestClass.StaticPublicField = v;
First overload will look like this.
public static Action<TField> StaticFieldSet<TSource, TField>(string fieldName) { var source = typeof(TSource); return source.StaticFieldSet<TField>(fieldName); }
The third one is much more interesting, but still nothing really hard to understand after indexers.
public static Action<object> StaticFieldSet(this Type source, string fieldName) { var fieldInfo = GetStaticFieldInfo(source, fieldName); if (fieldInfo != null) { var valueParam = Expression.Parameter(typeof(object)); var convertedValueExpr = Expression.Convert(valueParam, fieldInfo.FieldType); var lambda = Expression.Lambda(typeof(Action<object>), Expression.Assign(Expression.Field(null, fieldInfo), convertedValueExpr), valueParam); return (Action<object>)lambda.Compile(); } return null; }
As expected, value parameter expression is in this method of type object instead of the same type as field. Because of that, it needs to be converted first, before assignment. The same delegate as lambda for TestClass.StaticPublicField would be similar to lambda below.
Action<object> @delegate = v => TestClass.StaticPublicField = (string)v;
Time to test if those methods work.
var sfs1 = DelegateFactory.StaticFieldSet<TestClass, string>("StaticPublicField"); var sfs2 = Type.StaticFieldSet<string>("StaticPublicField"); var sfs3 = Type.StaticFieldSet("StaticPublicField"); sfs1("test1"); Console.WriteLine("Static public field value: {0}", TestClass.StaticPublicField); sfs2("test2"); Console.WriteLine("Static public field value: {0}", TestClass.StaticPublicField); sfs3("test3"); Console.WriteLine("Static public field value: {0}", TestClass.StaticPublicField);
The above code will result with console output as below.
Static public field value: test1 Static public field value: test2 Static public field value: test3
Fields
We already practiced on static fields so instance fields should not be a problem. The main change is extra parameter in all delegates. Because of that, we need not null first parameter (source instance) in Expression.Field call. Besides that, everything stays the same.
We should add some fields first to TestClass type.
public string PublicField; internal string InternalField; protected string ProtectedField; private string _privateField; public TestClass() { PublicField = "PublicField"; InternalField = "InternalField"; ProtectedField = "ProtectedField"; _privateField = "_privateField"; }
Get field value delegates
Of course, we want the same three methods for instance fields.
var fg1 = DelegateFactory.FieldGet<TestClass, string>("PublicField"); var fg2 = Type.FieldGet<string>("PublicField"); var fg3 = Type.FieldGet("PublicField");
First, we have to create a similar method to obtain FieldInfo object like in GetStaticFieldInfo.
private static FieldInfo GetFieldInfo(Type source, string fieldName) { var fieldInfo = (source.GetField(fieldName) ?? source.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic)) ?? source.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); return fieldInfo; }
Almost the same, but it uses Instance value of BindingFlags enumeration instead of Static one.
Let’s start with the last one FieldGet method this time – it should be most complex.
public static Func<object, object> FieldGet(this Type source, string fieldName) { var fieldInfo = GetFieldInfo(source, fieldName); if (fieldInfo != null) { var sourceParam = Expression.Parameter(typeof(object)); Expression returnExpression = Expression.Field(Expression.Convert(sourceParam, source), fieldInfo); if (!fieldInfo.FieldType.IsClass) { returnExpression = Expression.Convert(returnExpression, typeof(object)); } var lambda = Expression.Lambda(returnExpression, sourceParam); return (Func<object, object>)lambda.Compile(); } return null; }
It is similar code to PropertyGet method and StaticFieldGet. Returned Func takes instance to retrieve field as object, casts it to correct instance type and retrieves field value, which is casted to object if it is value type, i.e. for TestClass.PublicField it will create delegate like this.
Func<object, object> @delegate = i => ((TestClass)i).PublicField;
Now, the same method can create delegate for two other methods just by safe cast.
public static Func<TSource, TField> FieldGet<TSource, TField>(string fieldName) { var source = typeof(TSource); return source.FieldGet(fieldName) as Func<TSource, TField>; } public static Func<object, TField> FieldGet<TField>(this Type source, string fieldName) { return source.FieldGet(fieldName) as Func<object, TField>; }
I do not really investigate it further why this works (for example, with ILDASM application), but I suspect that excessive casting get optimized by JIT compilation, because there is not really a performance difference between produced delegates.
We can test new methods by following lines in test console application.
var fg1 = DelegateFactory.FieldGet<TestClass, string>("PublicField"); var fg2 = DelegateFactory.FieldGet<TestClass, string>("InternalField"); var fg3 = DelegateFactory.FieldGet<TestClass, string>("ProtectedField"); var fg4 = DelegateFactory.FieldGet<TestClass, string>("_privateField"); var fg5 = Type.FieldGet<string>("PublicField"); var fg6 = Type.FieldGet("PublicField"); Console.WriteLine("Public field value: {0}", fg1(TestInstance)); Console.WriteLine("Internal field value: {0}", fg2(TestInstance)); Console.WriteLine("Protected field value: {0}", fg3(TestInstance)); Console.WriteLine("Private field value: {0}", fg4(TestInstance)); Console.WriteLine("Public field value by object and field type: {0}", fg5(TestInstance)); Console.WriteLine("Public field value by objects: {0}", fg6(TestInstance));
It will produce the following output in Windows console.
Public field value: PublicField Internal field value: InternalField Protected field value: ProtectedField Private field value: _privateField Public field value by object and field type: PublicField Public field value by objects: PublicField
We can also test performance difference between direct field access and by proxy delegate.
_stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { var test = TestInstance.PublicField; } _stopWatch.Stop(); Console.WriteLine("Public field directly: {0}", _stopWatch.ElapsedMilliseconds); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { var test = fg1(TestInstance); } _stopWatch.Stop(); Console.WriteLine("Public field retriever: {0}", _stopWatch.ElapsedMilliseconds); var fieldInfo = Type.GetField("PublicField"); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { var test = fieldInfo.GetValue(TestInstance); } _stopWatch.Stop(); Console.WriteLine("Public field via reflection: {0}", _stopWatch.ElapsedMilliseconds);
This performance test will produce a similar result.
Public field directly: 257 Public field retriever: 545 Public field via reflection: 9721
As you can see the difference is huge – more than 100%. But after all, accessing public fields do not make sense this way, unless you do not know type at compilation time or field is not public at all. In those cases, it is better to have slower access but access nevertheless. Unless you are fine with even bigger performance difference with use of reflection which is about 20 times slower.
Set field value delegates
Since we already trained setting of values in properties and in static fields, setting values in instance fields should not be a problem.
We still want the similar three methods: static, extension by objects and extension with field type.
var fs1 = DelegateFactory.FieldSet2<TestClass, string>("_privateField"); var fs2 = Type.FieldSet<string>("_privateField"); var fs3 = Type.FieldSet("PublicField");
Ok this time we again will start with the last one that should be more complex. First two should be able to cast delegate from third.
public static Action<object, object> FieldSet(this Type source, string fieldName) { var fieldInfo = GetFieldInfo(source, fieldName); if (fieldInfo != null) { var sourceParam = Expression.Parameter(typeof(object)); var valueParam = Expression.Parameter(typeof(object)); var convertedValueExpr = Expression.Convert(valueParam, fieldInfo.FieldType); Expression returnExpression = Expression.Assign(Expression.Field(Expression.Convert(sourceParam, source), fieldInfo), convertedValueExpr); if (!fieldInfo.FieldType.IsClass) { returnExpression = Expression.Convert(returnExpression, typeof(object)); } var lambda = Expression.Lambda(typeof(Action<object, object>), returnExpression, sourceParam, valueParam); return (Action<object, object>)lambda.Compile(); } return null; }
As with instance properties, we have delegate in form of an Action with two parameters: first instance and then value to set. Both as objects. Similarly to static field, we assigning value to field by Expression.Assign and Expression.Field. Field has to take converted source parameter to instance type, instead of a null like in static field. Besides that code is very similar. Two other methods can be implemented by simple cast as in FieldGet.
public static Action<object, TProperty> FieldSet<TProperty>(this Type source, string fieldName) { return source.FieldSet(fieldName) as Action<object, TProperty>; } public static Action<TSource, TProperty> FieldSet<TSource, TProperty>(string fieldName) { return typeof(TSource).FieldSet(fieldName) as Action<TSource, TProperty>; }
It works, but according to tests, it is a bit slower than specific implementation. Version with delegate created directly, without casting are as follows.
public static Action<object, TProperty> FieldSet2<TProperty>(this Type source, string fieldName) { var fieldInfo = GetFieldInfo(source, fieldName); if (fieldInfo != null) { var sourceParam = Expression.Parameter(typeof(object)); var valueParam = Expression.Parameter(typeof(TProperty)); var te = Expression.Lambda(typeof(Action<object, TProperty>), Expression.Assign(Expression.Field(Expression.Convert(sourceParam, source), fieldInfo), valueParam), sourceParam, valueParam); return (Action<object, TProperty>)te.Compile(); } return null; } public static Action<TSource, TProperty> FieldSet2<TSource, TProperty>(string fieldName) { var source = typeof(TSource); var fieldInfo = GetFieldInfo(source, fieldName); if (fieldInfo != null) { var sourceParam = Expression.Parameter(source); var valueParam = Expression.Parameter(typeof(TProperty)); var te = Expression.Lambda(typeof(Action<TSource, TProperty>), Expression.Assign(Expression.Field(sourceParam, fieldInfo), valueParam), sourceParam, valueParam); return (Action<TSource, TProperty>)te.Compile(); } return null; }
Code for performance test, that led to this conclusion, looks like this.
var fs1 = DelegateFactory.FieldSet<TestClass, string>("PublicField"); var fs2 = DelegateFactory.FieldSet2<TestClass, string>("PublicField"); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { fs1(TestInstance, "test"); } _stopWatch.Stop(); Console.WriteLine("Public field set retriever with cast: {0}", _stopWatch.ElapsedMilliseconds); _stopWatch = new Stopwatch(); _stopWatch.Start(); for (var i = 0; i < _delay; i++) { fs2(TestInstance, "test"); } _stopWatch.Stop(); Console.WriteLine("Public field set retriever without cast: {0}", _stopWatch.ElapsedMilliseconds);
Which will result in text below.
Public field set retriever with cast: 751 Public field set retriever without cast: 475
As you can see, the difference is noticeable. Probably instead of using the same method, but with parameter casting/without casting, the correct delegate is created by creating a new method that casts/converts parameters to appropriate types.
Ok, we will use FieldSet2 methods and not FieldSet overloads but after renaming them to FieldSet and FieldSetWithCast accordingly.
Improvements
If you were wondering about readonly keyword in fields, it is a good thing. This thing should be covered in a similar way like null check for getter or setter method in properties. After all, readonly field is a similar to get-only property. Is there a way to find out if field is declared with readonly keyword? As you surely remember, readonly fields can be either set in field declaration or in constructor. Both ways are executed on object initialization. And FieldInfo type have property called IsInitOnly, which indicates if field can be set anytime or just during initialization – which is another way of telling if field is readonly. This means that instead of just null check in fieldInfo variable we also need to check for this property. In both: static fields and instance fields.
if (fieldInfo != null && !fieldInfo.IsInitOnly)
Of course, it is needed in all StaticFieldSet and FieldSet methods, i.e.
public static Action<object, TProperty> FieldSet<TProperty>(this Type source, string fieldName) { var fieldInfo = GetFieldInfo(source, fieldName); if (fieldInfo != null && !fieldInfo.IsInitOnly) { var sourceParam = Expression.Parameter(typeof(object)); var valueParam = Expression.Parameter(typeof(TProperty)); var te = Expression.Lambda(typeof(Action<object, TProperty>), Expression.Assign(Expression.Field(Expression.Convert(sourceParam, source), fieldInfo), valueParam), sourceParam, valueParam); return (Action<object, TProperty>)te.Compile(); } return null; }
Summary
Now we have a way to retrieve data from types by fields and properties, both static and instance. We also covered indexers which are special instance properties. What is left are events (instance not static), methods and constructors. Those types of members will be covered in the next article.
The code for article can be found on github and as Nuget package.
Note that "then" in the article title, should be "than", I think, unless you did that on purpose.
I did not and I am embarrassed. I did checked spelling before publishing, but I did not thought about checking spelling of title 🙁 Thanks!