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

时间:2010-04-12 11:04:28

标签: c# properties delegates

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

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;

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

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

有没有办法直接以编译安全的方式在属性获取方法上创建委托,类似于在顶部的普通方法上创建委托,而不需要使用中间lambda方法?

4 个答案:

答案 0 :(得分:7)

据我所知,你已经写下了所有“有效”的变体。由于无法在普通代码中明确地寻址getter或setter(没有反射,所以),我认为没有办法做你想做的事。

答案 1 :(得分:1)

诀窍是Property实际上只是隐藏的实际getter和/或setter方法的外观。编译器发出这些方法,并根据分别以 get _ set _ 为前缀的Property的名称命名它们。在下面的示例中,它将是int get_Value()void set_Value(int)。因此,绕过所谓的&#34;属性&#34;并直接采取这些方法。

使用getter和/或setter方法,我们有两个选择。

  • 我们可以创建一个绑定委托,其某些实例具有 this 值&#34;烧入。&#34;这类似于您对属性本身的期望,即此委托仅适用于访问该一个运行时实例。优点是,因为委托永久地绑定到它的实例,所以你不必传递额外的参数。

  • 另一个选项是创建与特定目标实例无关的委托。虽然这些调用与以前完全相同的property-accessor方法,但在这种情况下,委托本身的Target属性为空/ null。缺少任何 this 指针,未绑定的委托的方法签名会被更改,以显示着名的&#34; hidden this&#34 ;指针。

下面进一步讨论,但首先是代码。它说明了所有四种情况,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; }
};

n.b。,这是指本帖子底部包含的一些帮助代码

总结一下,&#34;真正的签名&#34;实例方法与绑定委托的情况相同,但取消了。作为第一个参数,绑定代理通过提供它们在Target属性中携带的实例来处理它。未绑定的委托是通用的,因此您不需要每个属性只需要一个getter / setter对。它们可用于在任何过去,现在或将来的运行时实例上访问该实例属性,但这意味着您必须显式传递所需的目标 this 对象作为第一个参数每次你调用getter / setter。

另请注意,即使此处的未绑定代理正在访问实例属性或方法,您实际上也不需要任何可行的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));
}

答案 2 :(得分:0)

另一个选项(在.NET 3.0及更高版本中)将使用DependencyProperty而不是传统属性。然后,您可以传递DependencyProperty对象(而不是传递代理),并在需要时调用GetValue()SetValue()

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

答案 3 :(得分:0)

已经花了几个小时对此进行了困惑,这是一种解决方案,适用于需要从另一个类中进行快速属性访问的情况。例如,如果您需要为以前未知的类编写缓存的属性映射,这些类不具备该CreateDelegate魔力的知识。

一个简单的无害数据类,例如:

public class DataClass
{
    public int SomeProp { get; set; }
    public DataClass(int value) => SomeProp = value;
}

通用访问器类,其中T1是包含属性的类的类型,而T2是该属性的类型,如下所示:

public class PropAccessor<T1, T2>
{
    public readonly Func<T1, T2> Get;
    public readonly Action<T1, T2> Set;

    public PropAccessor(string propName)
    {
        Type t = typeof(T1);
        MethodInfo getter = t.GetMethod("get_" + propName);
        MethodInfo setter = t.GetMethod("set_" + propName);

        Get = (Func<T1, T2>)Delegate.CreateDelegate(typeof(Func<T1, T2>), null, getter);
        Set = (Action<T1, T2>)Delegate.CreateDelegate(typeof(Action<T1, T2>), null, setter);
    }
}

然后您可以这样做:

var data = new DataClass(100);

var accessor = new PropAccessor<DataClass, int>("SomeProp");

log(accessor.Get(data));
accessor.Set(data, 200);
log(accessor.Get(data));

基本上,您可以在启动时通过反射遍历类,并为每个属性创建PropAccessors缓存,从而为您提供相当快的访问权限。

编辑:几个小时后。

最终得到这样的结果。 PropAccessor的抽象祖先是必需的,因此我实际上可以在Prop类中声明该类型的字段,而无需诉诸使用dynamic。最终比MethodInfo快10倍。调用getter和setter方法。

internal abstract class Accessor
{
    public abstract void MakeAccessors(PropertyInfo pi);
    public abstract object Get(object obj);
    public abstract void Set(object obj, object value);
}

internal class PropAccessor<T1, T2> : Accessor
{
    private Func<T1, T2>    _get;
    private Action<T1, T2>  _set;

    public override object Get(object obj) => _get((T1)obj);
    public override void Set(object obj, object value) => _set((T1)obj, (T2)value);

    public PropAccessor() { }

    public override void MakeAccessors(PropertyInfo pi)
    {
        _get = (Func<T1, T2>)Delegate.CreateDelegate(typeof(Func<T1, T2>), null, pi.GetMethod);
        _set = (Action<T1, T2>)Delegate.CreateDelegate(typeof(Action<T1, T2>), null, pi.SetMethod);
    }
}

internal class Prop
{
    public string name;
    public int length;
    public int offset;
    public PropType type;
    public Accessor accessor;
}

internal class PropMap
{
    public UInt16 length;
    public List<Prop> props;

    internal PropMap()
    {
        length = 0;
        props = new List<Prop>();
    }

    internal Prop Add(PropType propType, UInt16 size, PropertyInfo propInfo)
    {
        Prop p = new Prop()
        {
            name   = propInfo.Name,
            length = size,
            offset = this.length,
            type   = propType,
            Encode = encoder,
            Decode = decoder,
        };

        Type accessorType = typeof(PropAccessor<,>).MakeGenericType(propInfo.DeclaringType, propInfo.PropertyType);
        p.accessor = (Accessor)Activator.CreateInstance(accessorType);
        p.accessor.MakeAccessors(propInfo);

        this.length += size;
        props.Add(p);
        return p;
    }
}