简体   繁体   English

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

[英]Create a delegate from a property getter or setter method

To create a delegate from a method you can use the compile type-safe syntax: 要从方法创建委托,您可以使用compile type-safe语法:

private int Method() { ... }

// and create the delegate to Method...
Func<int> d = Method;

A property is a wrapper around a getter and setter method, and I want to create a delegate to a property getter method. 属性是getter和setter方法的包装器,我想创建一个属性getter方法的委托。 Something like 就像是

public int Prop { get; set; }

Func<int> d = Prop;
// or...
Func<int> d = Prop_get;

Which doesn't work, unfortunately. 不幸的是,这不起作用。 I have to create a separate lambda method, which seems unnecessary when the getter method matches the delegate signature anyway: 我必须创建一个单独的lambda方法,当getter方法匹配委托签名时,这似乎是不必要的:

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

In order to use the delegate method directly, I have to use nasty reflection, which isn't compile type-safe: 为了直接使用委托方法,我必须使用讨厌的反射,这不是编译类型安全的:

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

Is there any way of creating a delegate on a property getting method directly in a compile-safe way, similar to creating a delegate on a normal method at the top, without needing to use an intermediate lambda method? 有没有办法直接以编译安全的方式在属性获取方法上创建委托,类似于在顶部的普通方法上创建委托,而不需要使用中间lambda方法?

As far as I can tell, you have already written down all "valid" variants. 据我所知,你已经写下了所有“有效”的变种。 Since it isn't possible to explicitly address a getter or setter in normal code (without reflection, that is), I don't think that there is a way to do what you want. 由于无法在正常代码中明确地寻址getter或setter(没有反射,所以),我认为没有办法做你想要的。

The trick is that a Property is really just a facade on the actual getter and/or setter methods which are hidden. 诀窍在于, Property实际上只是隐藏的实际getter和/或setter方法的外观。 The compiler emits these method(s) and names them according to the name of the Property prepended with get_ and set_ , respectively. 编译器发出这些方法,并根据分别以get_set_为前缀的Property的名称命名它们。 In the example below it would be int get_Value() and void set_Value(int) . 在下面的示例中,它将是int get_Value()void set_Value(int) So just bypass the so-called "property" and just go straight for those methods. 因此,绕过所谓的“财产”,直接采取这些方法。

With either the getter and/or setter method we have two options. 使用getter和/或setter方法,我们有两个选择。

  • We can create a bound delegate which has the this value for some instance "burned-in." 我们可以创建一个绑定委托 ,该委托对于某些实例“烧入”具有this值。 This is similar to what you expect for the property itself, ie, this delegate will only be good for accessing that one runtime instance. 这类似于您对属性本身的期望,即此委托仅适用于访问该运行时实例。 The advantage is that, because the delegate is permanently bound to its instance, you don't have to pass in an extra argument. 优点是,因为委托永久绑定到其实例,所以您不必传入额外的参数。

  • The other option is to create delegates which aren't associated with a specific target instance. 另一种选择是创建与特定目标实例无关的委托。 Although these call the exact same property-accessor methods as before, in this case the Target property of the delegate itself is empty/null. 虽然这些调用与以前完全相同的属性访问器方法,但在这种情况下,委托本身的Target属性为空/ null。 Lacking any this pointer to use, the method signature for an unbound delegate is altered to reveal the famous " hidden this " pointer. 缺少任何this指针使用, 未绑定委托的方法签名被更改以显示着名的“ 隐藏此 ”指针。

Further discussion below, but first here's the code. 下面进一步讨论,但首先是这里的代码。 It illustrates all the four cases, getter/setter -vs- bound/unbound. 它说明了所有四种情况,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; }
};

nb, this refers to some helper code that's included at the bottom of this post nb,这是指包含在这篇文章底部的一些帮助代码

To summarize, the "true signature" of the instance method is identical to the bound delegate case, but gets cancelled off. 总而言之,实例方法的“真实签名”与绑定的委托案例相同,但会被取消。 Bound delegates take care of providing it, as the first argument, by supplying the instance they carry around in that Target property. 作为第一个参数,绑定代理通过提供它们在Target属性中携带的实例来处理它。 Unbound delegates are universal so you never need more than just a single getter/setter pair per-property. 未绑定的委托是通用的,因此您不需要每个属性只需要一个getter / setter对。 They can be used to to access that instance property on any past, present, or future runtime instance, but this means you have to explicitly pass a desired target this object in as the first argument every time you invoke the getter/setter. 它们可用于在任何过去,现在或将来的运行时实例上访问该实例属性,但这意味着每次调用getter / setter时,必须this对象作为第一个参数显式传递。

Note also that even though unbound delegates here are accessing instance properties or methods, you don't actually need any viable runtime instance of Cls to create the delegate. 另请注意,即使此处的未绑定委托正在访问实例属性或方法,您实际上也不需要任何可行的Cls运行时实例来创建委托。


Here's a demo. 这是一个演示。

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);
    }
};

And the output: 并输出:

 c1: 111 111 111 c2: 222 222 222 c1:111 111 111 c2:222 222 222\nc1: 555 555 555 c2: 666 666 666 c1:555 555 555 c2:666 666 666 

Finally, here's some helper stuff I put down here to reduce clutter. 最后,这里有一些帮助我放在这里的东西,以减少混乱。 Note that the MethodInfo s can be cached and reused if you plan on building lots of bound delegates. 请注意,如果您计划构建大量绑定委托,则可以缓存并重用MethodInfo If you instead prefer to use the unbound (static) delegates, you won't need to keep them around; 如果您更喜欢使用未绑定(静态)委托,则无需保留它们; because unbound delegates work universally for any instance, so you might decide you never need to create any bound delegates. 因为未绑定的委托对任何实例都是通用的,所以您可能决定永远不需要创建任何绑定的委托。

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));
}

Another option (in .NET 3.0 and newer) would be to use a DependencyProperty instead of a traditional property. 另一个选项(在.NET 3.0和更新版本中)将使用DependencyProperty而不是传统属性。 Then you can pass around the DependencyProperty object (instead of passing around a delegate), and call GetValue() or SetValue() when needed. 然后,您可以传递DependencyProperty对象(而不是传递委托),并在需要时调用GetValue()SetValue()

(Yes, I know this is an old question, but it was one of the top posts when I was trying to do something very similar.) (是的,我知道这是一个老问题,但当我尝试做一些非常相似的事情时,它是最重要的帖子之一。)

Having spent several hours puzzling this out, here is a solution for when you need to make fast property accessors from another class. 花了几个小时解决这个问题,这里有一个解决方案,当你需要从另一个类创建快速属性访问器时。 Such as if you need to write a cached property map for previously unknown classes that have no klnowledge of that CreateDelegate magic. 例如,如果您需要为以前未知的类编写缓存属性映射,而这些类没有该CreateDelegate魔法的知识。

A simple innocent data class, such as this one: 一个简单的无辜数据类,例如:

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

The universal accessor class, where T1 is the type of class that contains a property and T2 is the type of that property looks like this: 通用访问器类,其中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);
    }
}

And then you can do: 然后你可以这样做:

var data = new DataClass(100);

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

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

Basically, you can traverse your classes with reflection at startup and make a cache of PropAccessors for each property, giving you reasonably fast access. 基本上,您可以在启动时使用反射遍历类,并为每个属性创建PropAccessors缓存,从而为您提供合理的快速访问。

Edit: a few more hours later.. 编辑:再过几个小时..

Ended up with something like this. 结束了这样的事情。 The abstract ancestor to PropAccessor was necessary, so that I could actually declare a field of that type in the Prop class, without resorting to use of dynamic. PropAccessor的抽象祖先是必要的,这样我就可以在Prop类中声明该类型的字段,而不需要使用动态。 Ended up approximately 10x faster than with MethodInfo.Invoke for getters and setters. 对于getter和setter,使用MethodInfo.Invoke的速度提高约10倍。

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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM