从属性getter或setter方法创建委托
要从方法创建委托,您可以使用compile type-safe语法:
private int Method() { ... } // and create the delegate to Method... Func d = Method;
属性是getter和setter方法的包装器,我想创建一个属性getter方法的委托。 就像是
public int Prop { get; set; } Func d = Prop; // or... Func d = Prop_get;
不幸的是,这不起作用。 我必须创建一个单独的lambda方法,当getter方法匹配委托签名时,这似乎是不必要的:
Func d = () => Prop;
为了直接使用委托方法,我必须使用讨厌的reflection,这不是编译类型安全的:
// something like this, not tested... MethodInfo m = GetType().GetProperty("Prop").GetGetMethod(); Func d = (Func)Delegate.CreateDelegate(typeof(Func), m);
是否有任何方法可以直接以编译安全的方式在属性获取方法上创建委托,类似于在顶部的常规方法上创建委托,而无需使用中间lambda方法?
据我所知,你已经写下了所有“有效”的变种。 由于无法在正常代码中明确地寻址getter或setter(没有reflection,所以),我认为没有办法做你想要的。
另一个选项(在.NET 3.0和更新版本中)将使用DependencyProperty
而不是传统属性。 然后,您可以传递DependencyProperty
对象(而不是传递委托),并在需要时调用GetValue()
或SetValue()
。
(是的,我知道这是一个老问题,但当我尝试做一些非常相似的事情时,它是最重要的post之一。)
诀窍在于, Property
实际上只是隐藏的实际getter和/或setter方法的一个外观。 编译器发出这些方法,并根据分别以get_和set_为前缀的Property
的名称命名它们。 在下面的示例中,它将是int get_Value()
和void set_Value(int)
。 因此,绕过所谓的“财产”,直接采取这些方法。
使用getter和/或setter方法,我们有两个选择。
-
我们可以创建一个绑定委托 ,对于某些实例“烧入”具有
this
值。 这类似于您对属性本身的期望,即此委托仅适用于访问该运行时实例。 优点是,因为委托永久绑定到其实例,所以您不必传入额外的参数。 -
另一种选择是创建与特定目标实例无关的委托。 虽然这些调用与以前完全相同的属性访问器方法,但在这种情况下,委托本身的
Target
属性为空/ null。 缺少任何使用this
指针, 未绑定委托的方法签名被更改为显示着名的“ 隐藏此 ”指针。
下面进一步讨论,但首先是这里的代码。 它说明了所有四种情况,getter / setter -vs-bound / unbound。
partial class Cls { static Cls() { UnboundGet = create>(null, mi_get); UnboundSet = create>(null, mi_set); } public Cls() { BoundGet = create>(this, mi_get); BoundSet = create>(this, mi_set); } public readonly static Func UnboundGet; public readonly static Action UnboundSet; public readonly Func BoundGet; public readonly Action BoundSet; public int Value { get; set; } };
nb,这是指一些帮助代码,它包含在这篇文章的底部
总而言之,实例方法的“真实签名”与绑定的委托案例相同,但会被取消。 作为第一个参数,绑定代理通过提供它们在Target
属性中携带的实例来负责提供它。 未绑定的委托是通用的,因此您不需要每个属性只需要一个getter / setter对。 它们可用于在任何过去,现在或将来的运行时实例上访问该实例属性,但这意味着每次调用getter / setter时,必须this
对象作为第一个参数显式传递。
另请注意,即使此处的未绑定委托正在访问实例属性或方法,您实际上也不需要任何可行的Cls
运行时实例来创建委托。
这是一个演示。
static class demo { static demo() { var c1 = new Cls { Value = 111 }; var c2 = new Cls { Value = 222 }; Console.WriteLine("c1: {0} c2: {1}", c1, c2); c1.BoundSet(c1.Value + 444); Cls.UnboundSet(c2, c2.BoundGet() + 444); Console.WriteLine("c1: {0} c2: {1}", c1, c2); } };
并输出:
c1:111 111 111 c2:222 222 222 c1:555 555 555 c2:666 666 666
最后,这里有一些帮助我放在这里的东西,以减少混乱。 请注意,如果您计划构建大量绑定委托,则可以缓存并重用MethodInfo
。 如果您更喜欢使用未绑定(静态)委托,则无需保留它们; 因为未绑定的委托对任何实例都是通用的,所以您可能决定永远不需要创建任何绑定的委托。
partial class Cls { static MethodInfo mi_get = typeof(Cls).GetMethod("get_Value"), mi_set = typeof(Cls).GetMethod("set_Value"); static T create(Object _this, MethodInfo mi) => (T)(Object)Delegate.CreateDelegate(typeof(T), _this, mi); public override String ToString() => String.Format("{0} {1} {2}", Value, BoundGet(), Cls.UnboundGet(this)); }