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

从属性getter或setter方法创建委托

  •  19
  • thecoop  · 技术社区  · 16 年前

    要从方法创建委托,可以使用编译类型安全语法:

    private int Method() { ... }
    
    // and create the delegate to Method...
    Func<int> d = Method;
    

    属性是getter和setter方法的包装器,我想创建一个到属性getter方法的委托。有点像

    public int Prop { get; set; }
    
    Func<int> d = Prop;
    // or...
    Func<int> d = Prop_get;
    

    不幸的是,这不起作用。我必须创建一个单独的lambda方法,这在getter方法与委托签名匹配时似乎是不必要的:

    Func<int> d = () => Prop;
    

    为了直接使用delegate方法,我必须使用讨厌的反射,这不是编译类型安全的:

    // something like this, not tested...
    MethodInfo m = GetType().GetProperty("Prop").GetGetMethod();
    Func<int> d = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), m);
    

    是否有任何方法可以在属性获取方法上直接以编译安全的方式创建委托,类似于在顶部的普通方法上创建委托,而无需使用中间lambda方法?

    3 回复  |  直到 8 年前
        1
  •  7
  •   Lucero    16 年前

    据我所知,你已经写下了所有“有效”的变体。因为在普通代码中不可能显式地寻址getter或setter(没有反射,也就是说),所以我不认为有什么方法可以做您想要的事情。

        2
  •  1
  •   Glenn Slayden    8 年前

    诀窍在于 Property 实际上只是隐藏的实际getter和/或setter方法的外观。编译器发出这些方法并根据 财产 小精灵 塞特 ,分别是。在下面的例子中 int get_Value() void set_Value(int) . 所以绕过所谓的“属性”,直接使用这些方法。

    对于getter和/或setter方法,我们有两个选项。

    • 我们可以创造一个 绑定委托 其中有 this 某个实例的值“burned in”。这与您对属性本身的期望类似,即,此委托只适合访问该运行时实例。优点是,由于委托永久绑定到其实例,因此不必传入额外的参数。

    • 另一个选项是创建与特定目标实例无关的委托。尽管它们调用的属性访问器方法与以前完全相同,但在本例中 Target 委托本身的属性为空/空。缺乏任何 要使用的指针,方法签名为 未绑定委托 是为了揭示 hidden this “指针。

    下面将进一步讨论,但首先是代码。它说明了所有四种情况,getter/setter-vs-bound/unbound。

    partial class Cls
    {
        static Cls()
        {
            UnboundGet = create<Func<Cls, int>>(null, mi_get);
            UnboundSet = create<Action<Cls, int>>(null, mi_set);
        }
    
        public Cls()
        {
            BoundGet = create<Func<int>>(this, mi_get);
            BoundSet = create<Action<int>>(this, mi_set);
        }
    
        public readonly static Func<Cls, int> UnboundGet;
        public readonly static Action<Cls, int> UnboundSet;
    
        public readonly Func<int> BoundGet;
        public readonly Action<int> BoundSet;
    
        public int Value { get; set; }
    };
    

    注意,这指的是本文底部包含的一些帮助程序代码

    总之,实例方法的“真签名”与绑定的委托情况相同,但被取消。作为第一个参数,绑定委托通过提供它们在其中携带的实例来提供 靶标 财产。未绑定的委托是通用的,因此每个属性只需要一个getter/setter对。它们可用于访问任何过去、现在或将来运行时实例的实例属性,但这意味着您必须显式地传递所需的目标 每次调用getter/setter时,对象in作为第一个参数。

    还要注意,即使这里的未绑定委托正在访问 实例 属性或方法,实际上不需要 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<T>(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));
    }
    
        3
  •  0
  •   Erik Schierboom    10 年前

    另一个选项(在.NET3.0和更新版本中)是使用 DependencyProperty 而不是传统的财产。然后你可以绕过 关联属性 对象(而不是传递委托),并调用 GetValue() SetValue() 当需要时。

    (是的,我知道这是一个老问题,但当我试图做一些非常相似的事情时,这是最重要的一个问题。)