Creating a shared delegate using an expression in C #

The following are two methods that create a delegate for setting a field in a class. One method uses generics, and the other does not. Both methods return a delegate and they work fine. But if I try to use the delegate that was created in the CreateDelegate method, then the non-generic delegate "del" works fine. I can place a breakpoint in the return statement and call the delegate by calling del (222). But if I try to call the generic delegate 'genericDel', writing out genericDel (434), it throws an exception:

The delegate "System.Action" has some invalid arguments

Can someone explain this quirk.

class test { public double fld = 0; } public static void Main(string[] args) { test tst = new test() { fld = 11 }; Type myType = typeof(test); // Get the type and fields of FieldInfoClass. FieldInfo[] myFieldInfo = myType.GetFields(BindingFlags.Instance | BindingFlags.Public); var a = CreateDelegate<double>(myFieldInfo[0], tst); var b = CreateDelegate(myFieldInfo[0], tst); Console.WriteLine(tst.fld); b(5.0); Console.WriteLine(tst.fld); a(6.0); Console.WriteLine(tst.fld); } public static Action<T> CreateDelegate<T>(FieldInfo fieldInfo, object instance) { ParameterExpression numParam = Expression.Parameter(typeof(T), "num"); Expression a = Expression.Field(Expression.Constant(instance), fieldInfo); BinaryExpression assExp = Expression.Assign(a, numParam); Expression<Action<T>> expTree = Expression.Lambda<Action<T>>(assExp, new ParameterExpression[] { numParam }); Action<T> genericDel = expTree.Compile(); //try to invoke the delegate from immediate window by placing a breakpoint on the return below: genericDel(323) return genericDel; } public static Action<double> CreateDelegate(FieldInfo fieldInfo, object instance) { ParameterExpression numParam = Expression.Parameter(typeof(double), "num"); Expression a = Expression.Field(Expression.Constant(instance), fieldInfo); BinaryExpression assExp = Expression.Assign(a, numParam); Expression<Action<double>> expTree = Expression.Lambda<Action<double>>(assExp, new ParameterExpression[] { numParam }); Action<double> del = expTree.Compile(); //try to invoke the delegate from immediate window by placing a breakpoint on the return below: del(977) return del; } 
+4
source share
2 answers

I think I understood this problem; you are having problems calling the common delegate from the immediate window when the delegate compile time type is an open generic type. Here's a simpler reprogramming:

  static void Main() { Test<double>(); } static void Test<T>() { Action<T> genericDel = delegate { }; // Place break-point here. } 

Now, if I try to execute this delegate from the Test method (by placing a breakpoint and using the immediate window), like this:

 genericDel(42D); 

I get the following error:

 Delegate 'System.Action<T>' has some invalid arguments 

Please note that this is not an exception, as you stated, but rather a “immediate window version” compile time error CS1594 .

Note that such a call would not be the same at compile time, because there is no explicit or explicit conversion from double to T

This is inconsistent with the drawback of the direct window (it does not seem to want to use additional “knowledge at runtime” to help you in this case), but it can be argued that this is reasonable behavior, since the equivalent call made at compile time (in the original code) would also be illegal. However, it looks like a corner option; the direct window is perfectly capable of assigning shared variables and executing other code that would be illegal at compile time. Perhaps Roslyn will make things a lot more consistent.

If you want, you can work around this like this:

 genericDel.DynamicInvoke(42D); 

(or)

 ((Action<double>)(object)genericDel)(42D); 
+6
source

The problem is that you are trying to call a delegate as part of the method that creates it before the "T" is known. It tries to convert the value type (integer) to the generic type "T", which is not allowed by the compiler. If you think about it, that makes sense. You should only be able to go in T as long as you are within the framework of the method that the delegate creates, otherwise it would not be general at all.

You need to wait until the method returns, then use the delegate. You should not have a problem calling the delegate after it finishes:

 var a = CreateDelegate<double>(myFieldInfo[0], tst); var b = CreateDelegate(myFieldInfo[0], tst); a(434); 
0
source

All Articles