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

the*_*oop 19 c# delegates properties

要从方法创建委托,您可以使用compile type-safe语法:

private int Method() { ... }

// and create the delegate to Method...
Func<int> d = Method;
Run Code Online (Sandbox Code Playgroud)

属性是getter和setter方法的包装器,我想创建一个属性getter方法的委托.就像是

public int Prop { get; set; }

Func<int> d = Prop;
// or...
Func<int> d = Prop_get;
Run Code Online (Sandbox Code Playgroud)

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

Func<int> d = () => Prop;
Run Code Online (Sandbox Code Playgroud)

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

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

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

Luc*_*ero 7

据我所知,你已经写下了所有"有效"的变种.由于无法在正常代码中明确地寻址getter或setter(没有反射,所以),我认为没有办法做你想要的.

  • 没有办法让编译器使用代码中的直接引用来获取属性 getter 方法的 MethodInfo 吗?:( (2认同)

Avo*_*ppo 5

花了几个小时来解决这个问题,当您需要从另一个类创建快速属性访问器时,这里有一个解决方案。例如,如果您需要为以前未知的类编写缓存的属性映射,而这些类不了解 CreateDelegate 魔法。

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

public class DataClass
{
    public int SomeProp { get; set; }
    public DataClass(int value) => SomeProp = value;
}
Run Code Online (Sandbox Code Playgroud)

通用访问器类,其中 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

var data = new DataClass(100);

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

log(accessor.Get(data));
accessor.Set(data, 200);
log(accessor.Get(data));
Run Code Online (Sandbox Code Playgroud)

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

编辑:几个小时后..

最终得到了这样的事情。PropAccessor 的抽象祖先是必要的,这样我实际上可以在 Prop 类中声明该类型的字段,而无需使用动态。对于 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;
    }
}
Run Code Online (Sandbox Code Playgroud)