属性可以作为类型访问吗?

时间:2013-09-18 16:39:22

标签: c# .net properties lambda functional-programming

我可以将对象属性作为类型访问吗?

我正在使用API​​,我必须遍历一组对象,并访问其中两个对象的Text属性,用于读取或写入。我目前有两种阅读和写作方法如下:

Result ReadTexts()
    var attribs = SOME_CODE;
    string first = "", second = "";

    for(int i=1 ; i <= attribs.Count ; i++) {
        if(attribs[i] IS_FIRST_ONE_NEEDED) {
            first = attribs[i].Text;
        } else if(attribs[i] IS_SECOND_ONE_NEEDED) {
            second = attribs[i].Text;
        }
    }
    return new Result(first, second);
}

void WriteTexts(string first, string second) {
    var attribs = SOME_CODE;

    for(int i=1 ; i <= attribs.Count ; i++) {
        if(attribs[i] IS_FIRST_ONE_NEEDED) {
            attribs[i].Text = first;
        } else if(attribs[i] IS_SECOND_ONE_NEEDED) {
            attribs[i].Text = second;
        }
    }
}

我更喜欢的是使用更具功能性的样式,它将迭代的因素分解并将集合中的两个对象检查为一个方法,而不是重复此代码,因为实际上SOME_CODE以及IS_FIRST_ONE_NEEDED和IS_SECOND_ONE_NEEDED都有点长现实比上面的示例代码。这一种方法看起来像:

void AccessTexts(Action<StringProperty> first, Action<StringProperty> second) {
    var attribs = SOME_CODE;

    for(int i=1 ; i <= attribs.Count ; i++) {
        if(attribs[i] IS_FIRST_ONE_NEEDED) {
            first(attribs[i].Text);
        } else if(attribs[i] IS_SECOND_ONE_NEEDED) {
            second(attribs[i].Text);
        }
    }
}

然后使用lambda表达式(如

)调用它
AccessTexts(( prop => prop = "abc"), ( prop => prop = "def"));

写作,或

AccessTexts(( prop => firstString = prop), ( prop => secondString = prop));

阅读。这会更短,避免重复大量代码。

但我认为这是不可能的,因为属性不会在.net中作为真实类型公开,而只是基于特殊方法的可用性 - getter和setter。因此,没有类型StringProperty,因为我在“我想写的东西”的代码示例中使用它作为委托参数的类型。

我是对的,还是有某种方式以我想要的方式实现它?

2 个答案:

答案 0 :(得分:0)

你所做的绝对是可行的。您正在为您的函数AccessTexts提供相关属性的访问者,以便该函数不关心访问是如何完成的。

通常情况下,这可以通过迭代对象实现的接口来解决,或者由包装类实现。

您也可以使用反射或dynamic进行访问。

在任何情况下,您都需要AccessTexts与真实对象之间的代理。

答案 1 :(得分:0)

您可以创建自己的代表属性的类。如您所示,属性本质上只是一个get和set方法,因此我们所有的类都需要表示。

至于如何创建这样的东西,一个选择是让类型直接接受getter和setter作为委托。另一个选择是让它接受PropertyInfo和一个对象,然后可以使用反射来实现getter和setter方法。最后,如果您愿意,您甚至可以使用Expression来表示属性访问权限,然后从中提取PropertyInfo。

首先,开始使用实际的包装器本身:

public class PropertyWrapper<T>
{
    private Func<T> getter;
    private Action<T> setter;

    public PropertyWrapper(PropertyInfo property, object instance)
    {
        if (!typeof(T).IsAssignableFrom(property.PropertyType))
            throw new ArgumentException("Property type doesn't match type supplied");

        setter = value => property.SetValue(instance, value);
        getter = () => (T)property.GetValue(instance);
    }

    public PropertyWrapper(Func<T> getter, Action<T> setter)
    {
        this.setter = setter;
        this.getter = getter;
    }

    public T Get()
    {
        return getter();
    }

    public void Set(T value)
    {
        setter(value);
    }

    public T Value
    {
        get { return getter(); }
        set { setter(value); }
    }
}

然后你可以使用这样的帮助器(它从另一个类中提取出来,以便有通用的类型推断:

public class PropertyWrapper
{
    public static PropertyWrapper<TProp> Create<TObject, TProp>(
        TObject instance, Expression<Func<TObject, TProp>> expression)
    {
        var memberEx = expression.Body as MemberExpression;
        var prop = memberEx.Member as PropertyInfo;

        return new PropertyWrapper<TProp>(prop, instance);
    }
}

以下是使用两种不同语法构造此类对象的简单示例:

var list = new List<int>();

var prop1 = new PropertyWrapper<int>(
    () => list.Capacity, cap => list.Capacity = cap);

var prop2 = PropertyWrapper.Create(list, l => l.Capacity);

prop2.Value = 42;
Console.WriteLine(list.Capacity); //prints 42