将委托转换为Action<T>或Func<T>在运行时

10

我正在尝试通过为GetterSetter方法创建委托来改进我的反射代码。

我的代码看起来是这样的:

MyObject obj = new MyObject();
var prop = obj.GetType().GetProperty("Prop");
var getType = typeof(Func<>).MakeGenericType(prop.PropertyType);
var setType = typeof(Action<>).MakeGenericType(prop.PropertyType);

var getMethod = prop.GetGetMethod().CreateDelegate(getType, obj);
var setMethod = prop.GetSetMethod().CreateDelegate(setType, obj);

// I'd like to change this section and not to use a dynamic!!
dynamic castedGet = Convert.ChangeType(getMethod, getType);
dynamic castedSet = Convert.ChangeType(setMethod, setType);
CreateDelegate 返回一个 Delegate,而使用 DynamicInvoke 不是性能上的明智选择。
我将 Delegate 强制转换为 Action<T> \ Func<T>,并看到了巨大的性能提升。
然后我尝试在运行时将 Delegate 转换为 Action<T> \ Func<T>(使用 Convert.ChangeTypedynamic),但我的性能受到了影响——可能是因为我使用了 dynamic 类型。
我相信我可以不使用 dynamic 来完成这个操作。
我猜解决方案与 expression trees 有关,但我不太确定如何编写这样的代码。如果有人有一个不使用 expression trees 的好的解决方案,那么听起来很有趣。

@Dai - 这是我的代码的一小部分,也是一个示例。我想以一种懒加载的方式缓存每个属性的propertyinfo、getter和setter。 - Amir Popovich
@Dai - 我正在使用动态语言,因为我想以某种方式调用方法。 - Amir Popovich
所以,基本上你想要的是使用动态模板参数实例化一个对象? - Matteo Umili
@AlexeiLevenkov - 尽管它接近我需要的,但并不完全是我要找的。我尝试过自定义它以使之在我的环境中工作,但失败了。感谢有用的链接。 - Amir Popovich
@AmirPopovich:当您稍后想要调用getter/setter方法时,您是否处于一个上下文中,使得您的代码知道您期望的值的名称或类型?还是目的是在编译时让代码使用“object”值,当您知道这些值在运行时将是正确的类型时?也许解释一下您所设想的代码如何调用生成的“Action<>”并从生成的“Func<>”中检索值会有所帮助。 - StriplingWarrior
显示剩余4条评论
2个回答

3
如果您的目标是在不知道返回类型的情况下调用您的操作/函数,则可能希望最终得到一个Action<object>Func<object>,对吗?
您可以这样做,而无需编译表达式树或任何其他东西:
// Use reflection to create the action, invoking the method below.
var setAction = (Action<object>) this.GetType()
    .GetMethod("CastAction", BindingFlags.Static | BindingFlags.NonPublic)
    .MakeGenericMethod(prop.PropertyType)
    .Invoke(null, new object[]{setMethod});

// invoke the action like this:
object value = 42; // or any value of the right type.
setAction(value);

使用此辅助方法:
private static Action<object> CastAction<T>(Delegate d)
{
    var action = (Action<T>)d;
    return obj => action((T)obj);
}

我的测试结果显示,这种方法比使用 dynamic 大约快 25%,但比直接说 obj.Prop = 2 慢了约 45%;


@k3b的回答和OP说的“使用MethodInfo.Invoke比创建委托并调用它们要慢得多”有什么区别? - Matteo Umili
@codroipo:在这种情况下,我只是使用MethodInfo.Invoke()来创建委托。这实际上是OP的ChangeType方法在幕后执行的操作。然而,生成的setAction动作可以比调用setter.Invoke()更快地调用,就像k3b的答案一样。它比调用动态的castedSet动作稍微快一些,因为它不依赖于动态调用。 - StriplingWarrior
1
@StriplingWarrior - 这非常有趣。基准测试比使用动态方式更好,但比使用硬编码转换要慢。目前这是我所拥有的最快速的通用方法。感谢您的回答。我正在等待更多的答案,如果没有更好的,我会接受这个答案。谢谢!!!+1。 - Amir Popovich
1
@StriplingWarrior 好的,今天早上头脑清醒后我明白你在做什么了。肯定加一。 - Matteo Umili

1

你需要使用Action<T>或Func<T>来动态获取/设置属性吗?有什么原因吗?

如果不需要,你可以使用PropertyInfo.GetMethod()和SetMethod()。

 MyObject obj = new MyObject();
 PropertyInfo prop = obj.GetType().GetProperty("Prop");

 MethodInfo getter = prop.GetMethod();
 getter.Invoke(...)

 MethodInfo setter = prop.SetMethod();
 setter.Invoke(...)

1
原因是性能。使用MethodInfo.Invoke比创建委托并调用它们要慢得多(假设我想缓存它们)。无论如何,谢谢。 - Amir Popovich
1
我之前并不知道(也从未想过或检查过)使用Invoke()比使用dynamic进行鸭子类型更慢。 - k3b

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接