我可以将对象属性作为类型访问吗?
我正在使用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
,因为我在“我想写的东西”的代码示例中使用它作为委托参数的类型。
我是对的,还是有某种方式以我想要的方式实现它?
答案 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