答案 0 :(得分:15)
我喜欢使用表达式树来解决这个问题。只要您有一个想要获取“属性委托”的方法,请使用参数类型Expression<Func<T, TPropertyType>>
。例如:
public void SetPropertyFromDbValue<T, TProperty>(
T obj,
Expression<Func<T, TProperty>> expression,
TProperty value
)
{
MemberExpression member = (MemberExpression)expression.Body;
PropertyInfo property = (PropertyInfo)member.Member;
property.SetValue(obj, value, null);
}
关于这一点的好处是,获取的语法看起来也一样。
public TProperty GetPropertyFromDbValue<T, TProperty>(
T obj,
Expression<Func<T, TProperty>> expression
)
{
MemberExpression member = (MemberExpression)expression.Body;
PropertyInfo property = (PropertyInfo)member.Member;
return (TProperty)property.GetValue(obj, null);
}
或者,如果你感到懒惰:
public TProperty GetPropertyFromDbValue<T, TProperty>(
T obj,
Expression<Func<T, TProperty>> expression
)
{
return expression.Compile()(obj);
}
调用看起来像:
SetPropertyFromDbValue(myClass, o => o.Property1, reader["field1"]);
GetPropertyFromDbValue(myClass, o => o.Property1);
答案 1 :(得分:11)
(添加第二个答案,因为它采用完全不同的方法)
要解决原始问题,更多的是想要一个漂亮的API来将datareader中的命名值映射到对象的属性,请考虑System.ComponentModel.TypeDescriptor
- 一个经常被忽视的替代方法,自己做反思性的脏工作。
这是一个有用的片段:
var properties = TypeDescriptor.GetProperties(myObject)
.Cast<PropertyDescriptor>()
.ToDictionary(pr => pr.Name);
创建对象的属性描述符字典。
现在我可以这样做:
properties["Property1"].SetValue(myObject, rdr["item1"]);
PropertyDescriptor
的SetValue方法(与System.Reflection.PropertyInfo
等价的方法不同)将为您进行类型转换 - 将字符串解析为整数,依此类推。
对此有用的是可以想象一种属性驱动的方法来迭代该属性集合(PropertyDescriptor
具有Attributes
属性,以允许您获取添加到属性的任何自定义属性找出要使用的datareader中的哪个值;或者有一个接收propertyname字典的方法 - columnname mappings迭代并为你执行所有这些集合。
我怀疑像这样的方法可能会为你提供所需的API快捷方式,而lambda-expression反射技巧 - 在这种情况下 - 不会。
答案 2 :(得分:8)
忽略这在您的具体情况下是否有用(我认为您采用的方法效果很好),您的问题是“有没有办法将属性转换为委托”。
嗯,有种可能。
实际上(幕后)每个属性都包含一个或两个方法 - set方法和/或get方法。你可以 - 如果你能掌握这些方法 - 让代表们把它们包起来。
例如,一旦您在System.Reflection.PropertyInfo
类型的对象上拥有代表TProp
类型属性的TObj
对象,我们就可以创建Action<TObj,TProp>
(即,一个委托,它接受一个设置属性的对象和一个设置它的值),它包装了这个setter方法,如下所示:
Delegate.CreateDelegate(typeof (Action<TObj, TProp>), propertyInfo.GetSetMethod())
或者我们可以创建一个Action<TProp>
,它将setter包装在TObj
的特定实例上,如下所示:
Delegate.CreateDelegate(typeof (Action<TProp>), instance, propertyInfo.GetSetMethod())
我们可以使用static reflection扩展方法包装那么一点:
public static Action<T> GetPropertySetter<TObject, T>(this TObject instance, Expression<Func<TObject, T>> propAccessExpression)
{
var memberExpression = propAccessExpression.Body as MemberExpression;
if (memberExpression == null) throw new ArgumentException("Lambda must be a simple property access", "propAccessExpression");
var accessedMember = memberExpression.Member as PropertyInfo;
if (accessedMember == null) throw new ArgumentException("Lambda must be a simple property access", "propAccessExpression");
var setter = accessedMember.GetSetMethod();
return (Action<T>) Delegate.CreateDelegate(typeof(Action<T>), instance, setter);
}
现在我可以为这样的对象获取一个'setter'委托:
MyClass myObject = new MyClass();
Action<string> setter = myObject.GetPropertySetter(o => o.Property1);
这是强类型的,基于属性本身的类型,因此在面对重构和编译时的类型检查时它是健壮的。
当然,在你的情况下,你希望能够使用一个可能为null的对象来设置你的属性,所以围绕setter的强类型包装并不是整个解决方案 - 但它确实给你一些东西要传递给你的SetPropertyFromDbValue
方法。
答案 3 :(得分:6)
不,没有什么类似于属性的方法组转换。您可以做的最好的事情是使用lambda表达式来形成Func<string>
(对于getter)或Action<string>
(对于setter):
SetPropertyFromDbValue<string>(value => myClass.Property1 = value,
rdr["field1"]);
答案 4 :(得分:2)
值得一提的是你可以用一些反思技巧来做到这一点。 类似......
public static void LoadFromReader<T>(this object source, SqlDataReader reader, string propertyName, string fieldName)
{
//Should check for nulls..
Type t = source.GetType();
PropertyInfo pi = t.GetProperty(propertyName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
object val = reader[fieldName];
if (val == DBNull.Value)
{
val = default(T);
}
//Try to change to same type as property...
val = Convert.ChangeType(val, pi.PropertyType);
pi.SetValue(source, val, null);
}
然后
myClass.LoadFromReader<string>(reader,"Property1","field1");
答案 5 :(得分:0)
正如其他人所指出的那样,静态反思是可行的方法。
这些课程开箱即用:
http://www.codeproject.com/Articles/36262/Getting-Fun-with-Net-Static-Reflection.aspx