代码之家  ›  专栏  ›  技术社区  ›  Giorgi

具有未知类型的CreateDelegate

  •  11
  • Giorgi  · 技术社区  · 14 年前

    我正在尝试为运行时未知类型的类的读/写属性创建委托。

    我有一个普通班 Main<T> 一种方法是这样的:

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

    在哪里? get 是一个 MethodInfo 应读取的属性。问题是当属性返回时 int (我猜对于值类型会发生这种情况)上述代码引发ArgumentException,因为无法绑定该方法。如果是绳子,它会很好地工作。

    为了解决这个问题,我更改了代码,以便通过使用 MakeGenericType . 现在代码是:

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

    现在的问题是 generic 所以我必须使用 DynamicInvoke 这就像用纯反射来读取磁场一样慢。

    所以我的问题是为什么第一段代码在值类型上失败了。根据 MSDN 它应该像它说的那样工作

    如果方法的返回类型比委托的返回类型更具限制性,则委托的返回类型与方法的返回类型兼容。

    以及如何在第二个代码片段中执行委托,以便它比反射更快。

    谢谢。

    4 回复  |  直到 14 年前
        1
  •  10
  •   kvb    14 年前

    这里有一种方法可以解决你的问题。创建通用方法:

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

    这样,C的编译器就可以插入必要的装箱(如果有的话)来转换 f(t) (属于类型) U object . 现在你可以用反射来称之为 MakeDelegate 方法 U 设置为 @get.ReturnType 你得到的将是 Func<T, object> 无需使用即可调用 DynamicInvoke .

        2
  •  3
  •   Hans Passant    14 年前

    原始代码只能用于引用类型。这就是为什么字符串不是问题的原因,它直接从System.Object派生。值类型是从值类型和对象派生的,这在纸面上是一种很好的假象,但实际上需要代码。C编译器自动发出该代码,它需要装箱转换。这是这里缺少的部分,没有从int到object的运行时转换 BOX opcode .

    您可以在代码中获得该操作码,但必须使用System.Reflection.Emit。

    在你去那里之前,先检查一下你现在的速度是否真的太慢了。反射的开销是从程序集中挖掘元数据。这是在创建委托时完成的,然后缓存类型信息。

        3
  •  2
  •   kvb    14 年前

    调用失败,因为需要的对象不是值类型(如int)——显然 Func<T, int> 不是 Func<T, Int> -它不适用于任何VT,如Double或Bool。或者返回一个装箱的int(或者任何你有的vt)。或者(也许更好)使用反射Emit API。

    通过使用反射发射类,可以创建动态方法并将其保存为委托,或者创建动态委托并将其保存在某些结构中。您只能这样做一次(可能每个运行时一次),将它存储在某个dict中,并在需要时调用。

    希望有帮助。 卢克

        4
  •  0
  •   Fiona - myaccessible.website    14 年前

    您是否可以将泛型方法限制为仅使用引用类型,并创建另一个方法来仅使用值类型,并相应地决定使用哪个功能?