views:

328

answers:

4

Hello,

I am trying to create Delegate for reading/writing properties of unknown type of class at runtime.

I have a generic class Main<T> and a method which looks like this:

Delegate.CreateDelegate(typeof(Func<T, object>), get)

where get is a MethodInfo of the property that should be read. The problem is that when the property returns int (I guess this happens for value types) the above code throws ArgumentException because the method cannot be bound. In case of string it works well.

To solve the problem I changed the code so that corresponding Delegate type is generated by using MakeGenericType. So now the code is:

Type func = typeof(Func<,>);
Type generic = func.MakeGenericType(typeof(T), get.ReturnType);
var result = Delegate.CreateDelegate(generic, get)

The problem now is that the created delegate instance of generic so I have to use DynamicInvoke which would be as slow as using pure reflection to read the field.

So my question is why is that the first snippet of code fails with value types. According to MSDN it should work as it says that

The return type of a delegate is compatible with the return type of a method if the return type of the method is more restrictive than the return type of the delegate

and how to execute the delegate in the second snippet so that it is faster than reflection.

Thanks.

+1  A: 

You invocation fails because You require object not a value type (like INT) -- obviously Func<T, int> is not a Func<T, Int> - it won't work with any vt like double or bool. Either return a boxed Int (or whatever vt You've got) . or (perhaps better) use reflection emit API.

By using reflection emit classes You can create dynamic methods and save them as delegates, or create Dynamic delegates and save them in some of Your structure. You could do this only once (perhaps once per runtime) store it in some Dict and invoke when needed.

hope it helps. luke

luckyluke
All value types inherit from object so that's why I thought it would work.I am aware of reflection emit but it is more complicated compared to CreateDelegate
Giorgi
but covariance and contravariance DOES NOT work with value types. That is all the purpose of value types and boxing. http://msdn.microsoft.com/en-us/magazine/cc163727.aspxbecause there is no IMPLICIT conversionmore herehttp://blogs.msdn.com/ericlippert/archive/2007/10/19/covariance-and-contravariance-in-c-part-three-member-group-conversion-variance.aspx
luckyluke
+2  A: 

Your original code can only work for reference types. That's why string wasn't a problem, it directly derives from System.Object. That a value type derives from ValueType and Object is a nice illusion on paper but actually requires code. The C# compiler automatically emits that code, it requires a boxing conversion. That's the part that is missing here, there is no runtime conversion from int to object without the BOX opcode.

You can get that opcode in your code, but you'll have to use System.Reflection.Emit.

Before you go there, first check if what you've got now is actually too slow. The expense of reflection is digging the metadata out of the assembly. That was done when you created the delegate, the type info is cached after that.

Hans Passant
So you mean that using DynamicInvoke on the delegate generated in snippet two is fast or am I mistaken?Currently I am using SetValue/GetValue to write/read properties and wanted to make it faster.
Giorgi
A: 

Is it possible for you to restrict the generic method to only work with reference types, and create another one to only work with value types, and decide which functionality to use accordingly?

Fiona Holder
+2  A: 

Here's one way to solve your problem. Create a generic method:

public static Func<T, object> MakeDelegate<U>(MethodInfo @get)
{
    var f = (Func<T, U>)Delegate.CreateDelegate(typeof(Func<T, U>), @get);
    return t => f(t);
}

This way, C#'s compiler takes care of inserting the necessary boxing (if any) to convert f(t) (of type U) to object. Now you can use reflection to call this MakeDelegate method with U set to @get.ReturnType, and what you get back will be a Func<T, object> which can be called without needing to resort to using DynamicInvoke.

kvb
Thanks very much, it worked!
Giorgi