在c#中使用表达式创建泛型委托

本文关键字:创建 泛型 表达式 | 更新日期: 2023-09-27 18:11:56

下面给出了创建委托来设置类中的字段的两个方法。一种方法使用泛型,另一种不使用。这两个方法都返回一个委托,它们工作得很好。但如果我尝试使用在CreateDelegate功能中创建的委托,那么非泛型委托del工作得很好。我可以在返回语句上放置一个断点,并通过编写del(222)来调用委托。但是,如果我试图通过编写genericDel(434)来调用泛型委托'genericDel',它会抛出一个异常:

委托的系统。

操作有无效参数

有谁能解释一下这个怪癖吗?

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;
}

在c#中使用表达式创建泛型委托

我想我明白了这个问题;当委托的编译时类型是打开的泛型类型时,从立即窗口调用泛型委托会遇到问题。下面是一个更简单的示例:

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

现在,如果我尝试从Test方法中执行这个委托(通过放置一个断点并使用即时窗口),像这样:

genericDel(42D);

我得到以下错误:

Delegate 'System.Action<T>' has some invalid arguments
请注意,这个不是像你所说的一个异常,而是编译时错误CS1594的"即时窗口版本"。

注意这样的调用在编译时同样会失败,因为没有从doubleT的隐式或显式转换。

这是有争议的即时窗口的一个缺点(在这种情况下,它似乎不愿意使用额外的"运行时知识"来帮助您),但有人可能会争辩说这是合理的行为,因为在编译时(在源代码中)进行的等效调用也是非法的。不过,这似乎是一个角落案例;即时窗口完全能够赋值泛型变量和执行在编译时可能是非法的其他代码。也许Roslyn会让事情更加一致。

如果你愿意,你可以这样处理:

genericDel.DynamicInvoke(42D);

(或)

((Action<double>)(object)genericDel)(42D);

问题是,在'T'已知之前,您试图在创建它的方法范围内调用委托。它试图将值类型(整数)转换为编译器不允许的泛型'T'。如果你仔细想想,这是有道理的。你应该只能够传入T,只要你在创建委托的方法范围内,否则它就不是真正的泛型。

你需要等待方法返回,然后使用委托。在委托完成后调用它应该没有问题:

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