如何在不使用反射的情况下创建泛型属性访问器

本文关键字:泛型 创建 属性 访问 情况下 反射的 | 更新日期: 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);

就个人而言,我更喜欢基于表达式的版本。