0

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

我正在使用一个 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,因为我在“我想写的”的代码示例中将其用作委托参数的类型。

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

4

2 回答 2

0

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

通常,这将使用由被迭代的对象实现的接口或由包装类实现的接口来解决。

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

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

于 2013-09-18T16:50:24.633 回答
0

您可以创建自己的代表属性的类。正如您所展示的,属性本质上只是一个 get 和 set 方法,所以这就是我们的类需要表示的所有内容。

至于如何创建这样的东西,一种选择是让类型直接接受 getter 和 setter 作为委托。另一种选择是让它接受一个PropertyInfo和一个对象,然后可以使用反射来实现 getter 和 setter 方法。最后,如果您愿意,您甚至可以使用 anExpression来表示属性访问,然后从中提取 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
于 2013-09-18T17:13:51.440 回答