This article is the first part of the series of three. You can find the other parts 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, 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 a 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 the 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 an 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 internal
s 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 work 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 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 involve 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 the 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 the 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 does 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 will 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 the 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), the 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 a 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 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 a 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 the 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 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 a more 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 a 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 lack some features. 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 create 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 just 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 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 lambda 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 besides 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 needs 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 are 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 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 this article can be found on github and as Nuget package.