如何在不使用反射的情况下创建泛型属性访问器
本文关键字:泛型 创建 属性 访问 情况下 反射的 | 更新日期: 2023-09-27 18:31:23
如何在不使用反射的情况下创建泛型属性访问器?
我想创建一个通用属性访问器类,以便能够将属性对象传递给它,然后访问该属性值;获取和设置。
我不喜欢只将反射与 Func 和 Action 以及 lambda 表达式一起使用。
以下代码的问题在于它复制了属性值并将其传递过来,因此它实际上不会发送属性引用,而我希望能够使用 GenericPropertyAccessor 类获取和设置 Product 对象的 Number 属性。
static void Main(string[] args)
{
var product = new Product {Number = 1};
var productNumberAccesstor = new GenericPropertyAccessor<int>(product.Number);
productNumberAccesstor.Value = 23;
Console.WriteLine(productNumberAccesstor.Value);
Console.ReadLine();
}
}
public class Product
{
public int Number { get; set; }
public string Name { get; set; }
}
public class GenericPropertyAccessor<T>
{
private T _property;
public GenericPropertyAccessor(T propertyExpression)
{
_property = propertyExpression;
}
public T Value
{
get
{
return Get(() => _property);
}
set
{
Set(valueInput => _property = valueInput , value);
}
}
static T Get(Func<T> getFunc)
{
return getFunc();
}
static void Set(Action<T> setAction, T value)
{
setAction(value);
}
}
基本上,我应该传递给 GenericPropertyAccessor 的构造函数才能启用此功能?
我知道一种解决方案如下,但是否有可能进一步简化它?特别是在 GenericPropertyAccessor 的构造函数上。是否可以将 lambda 表达式移动到类中并仅传递一种属性引用。
class Program
{
static void Main(string[] args)
{
var product = new Product {Number = 1};
var productNumberAccesstor = new GenericPropertyAccessor<int>(() => product.Number, value => product.Number = value);
productNumberAccesstor.Value = 23;
Console.WriteLine(productNumberAccesstor.Value);
Console.ReadLine();
}
}
public class Product
{
public int Number { get; set; }
}
public class GenericPropertyAccessor<T>
{
private readonly Func<T> _getFunc;
private readonly Action<T> _setAction;
public GenericPropertyAccessor(Func<T> getFunc, Action<T> setAction)
{
_getFunc = getFunc;
_setAction = setAction;
}
public T Value
{
get
{
return _getFunc();
}
set
{
_setAction(value);
}
}
}
我的意思是如何通过"产品"。数字"的表达在类内?
您需要传入当前在内部创建的委托:
public class GenericPropertyAccessor<T>
{
private Func<T> _getter;
private Action<T> _setter;
public GenericPropertyAccessor(Func<T> getter, Action<T> setter)
{
_getter = getter;
_setter = setter;
}
public T Value
{
get
{
return _getter();
}
set
{
_setter(value);
}
}
}
用法:
new GenericPropertyAccessor<int>(() => product.Number, v => product.Number = v);
以下是实现此目的的两种方法:一种使用 LINQ Expression
s,另一种使用普通的 Lambda 函数。 表达式比 Lambda 更容易调用,但从使用的角度来看,两者都可以进一步简化。
我们可以创建一个工厂类来简化调用:
// the Factory method:
public static class Factory
{
// Wrapper for Expression-based accessor:
public static Accessor<T> Accessor<T>(Expression<Func<T>> e)
{
return new Accessor<T>(e);
}
// Wrapper for Lambda-based accessor:
public static Accessor<T> Accessor<T>(Func<T> _get, Action<T> _set)
{
return new Accessor<T>(_get, _set)
}
}
使用表达式
如果对使用反射的限制未扩展到 LINQ 表达式,则可以通过仅传入一个() => object.Property
样式的 getter 表达式并从中生成资源库来进一步简化调用,如下所示:
public class Accessor<TProp> : IAccessor<TProp>
{
private readonly Func<TProp> _getter;
private readonly Action<TProp> _setter;
internal Accessor(Expression<Func<TProp>> e)
{
if (!(e.Body is System.Linq.Expressions.MemberExpression))
throw new ArgumentException("Invalid expression type supplied.", "e");
MemberExpression memacc = (MemberExpression)e.Body;
if (memacc.Type != typeof(TProp))
throw new ArgumentException("Invalid expression return type.", "e");
var parmValue = Expression.Parameter(typeof(TProp), "value");
var assign = Expression.Assign(memacc, parmValue);
_getter = e.Compile();
_setter = Expression.Lambda<Action<TProp>>(assign, new[] { parmValue }).Compile();
}
public TProperty Value
{
get { return _getter(_instance); }
set { _setter(_instance, value); }
}
}
// usage:
var obj = new TextBox();
var acc = Factory.Accessor(() => obj.Enabled);
Expression
编译器支持使这成为可能。 不是将lambda一直编译为代码,而是将其编译为表达式树并传递。 这个表达式的形式使我们能够非常简单地轻松创建我们需要的 get 和 set 方法。
没有表达式
如果不使用表达式,您几乎只能手动提供 get 和 set 方法。 将以下构造函数添加到 Accessor 类:
internal Accessor(Func<TProp> getter, Action<TProp> setter)
{
_getter = getter;
_setter = setter;
}
用法
要生成访问器,只需调用相应的工厂函数:
// Object to work on
var obj = new TextBox();
// accessor using Expressions
var acc1 = Factory.Accessor(() => obj.Enabled);
// accessor using Lambdas
var acc2 = Factory.Accessor() => obj.Enabled, v => obj.Enabled = v);
就个人而言,我更喜欢基于表达式的版本。