问题 具有未知类型的CreateDelegate


我正在尝试创建Delegate用于在运行时读取/写入未知类型的类的属性。

我有一个通用类 Main<T> 和一个看起来像这样的方法:

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

哪里 get 是一个 MethodInfo 应该阅读的财产。问题在于物业何时返回 int (我猜这种情况发生在值类型中)上面的代码抛出了ArgumentException,因为该方法无法绑定。在字符串的情况下,它运作良好。

为了解决这个问题,我更改了代码,以便使用生成相应的Delegate类型 MakeGenericType。所以现在的代码是:

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

现在的问题是创建的委托实例 generic 所以我必须使用 DynamicInvoke 这与使用纯反射来读取场地一样慢。

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

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

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

谢谢。


8203
2018-03-22 08:42


起源



答案:


这是解决问题的一种方法。创建一个通用方法:

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


10
2018-03-22 16:23



非常感谢,它有效! - Giorgi


答案:


这是解决问题的一种方法。创建一个通用方法:

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


10
2018-03-22 16:23



非常感谢,它有效! - Giorgi


您的原始代码只适用于参考类型。这就是字符串不是问题的原因,它直接来自System.Object。值类型派生自ValueType和Object在纸上是一个很好的错觉,但实际上需要代码。 C#编译器会自动发出该代码,它需要一个装箱转换。这是这里缺少的部分,没有运行时从int到object的转换 BOX操作码

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

在你去那里之前,首先检查你现在所拥有的是否真的太慢了​​。反射的代价是将元数据从组件中挖出。这是在您创建委托时完成的,之后缓存了类型信息。


3
2018-03-22 12:26



所以你的意思是在片段2中生成的委托上使用DynamicInvoke很快或者我错了?目前我正在使用SetValue / GetValue来编写/读取属性,并希望使其更快。 - Giorgi


您的调用失败,因为您需要对象而不是值类型(如INT) - 显然 Func<T, int> 不是一个 Func<T, Int>  - 它不适用于任何vt,如double或bool。要么返回一个盒装的Int(或者你得到的任何vt)。或者(可能更好)使用反射发射API。

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

希望能帮助到你。 卢克


2
2018-03-22 09:07



所有值类型都继承自object,这就是我认为它可行的原因。我知道反射发射,但与CreateDelegate相比,它更复杂 - Giorgi
但协方差和逆变不适用于价值类型。这是价值类型和拳击的所有目的。 msdn.microsoft.com/en-us/magazine/cc163727.aspx 因为这里没有更多的IMPLICIT转换 blogs.msdn.com/ericlippert/archive/2007/10/19/... - luckyluke


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


0
2018-03-22 14:17