Потратив несколько часов на то, чтобы разобраться в этом, вот решение для тех случаев, когда вам нужно создать быстрый метод доступа к собственности из другого класса. Например, если вам нужно написать кэшированную карту свойств для ранее неизвестных классов, у которых нет знаний об этой магии 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, не прибегая к использованию динамического. Завершается примерно в 10 раз быстрее, чем с MethodInfo.Invoke для геттеров и сеттеров.
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;
}
}