我如何作为代表传递财产?

这是一个理论问题,我已经找到了解决问题的方法,这使我走上了不同的道路,但我认为这个问题仍然很有意思。

我可以像对待方法一样将对象属性作为委托传递吗? 例如:

假设我有一个数据读取器加载了数据,并且每个字段的值需要传递到已经检查过DBNull的不同类型的属性中。 如果试图获得单个字段,我可能会写如下:

if(!rdr["field1"].Equals(DBNull.Value)) myClass.Property1 = rdr["field1"]; 

但是,如果我说100个字段,那很快就会变得笨拙。 执行此操作的调用有两种方式可能看起来不错:

 myClass.Property = GetDefaultOrValue(rdr["field1"]); //Which incidentally is the route I took 

作为扩展方法,它看起来也很不错:

 myClass.Property = rdr["field1"].GetDefaultOrValue(); 

要么:

 SetPropertyFromDbValue(myClass.Property1, rdr["field1"]); //Which is the one that I'm interested in on this theoretical level 

在第二个实例中,属性需要作为委托传递才能进行设置。

所以问题分为两部分:

  1. 这可能吗?
  2. 那会是什么样的?

[因为这只是理论上的,VB或C#中的答案对我来说同样可以接受]

编辑:这里有一些光滑的答案。 谢谢大家。

(添加第二个答案,因为它采用完全不同的方法)

为了解决您的原始问题,更多的是想要一个很好的API来将datareader中的命名值映射到对象的属性,请考虑System.ComponentModel.TypeDescriptor – 一个经常被忽视的替代方法,自己做反思性的脏工作。

这是一个有用的片段:

 var properties = TypeDescriptor.GetProperties(myObject) .Cast() .ToDictionary(pr => pr.Name); 

这会创建对象的propertydescriptors字典。

现在我可以这样做:

 properties["Property1"].SetValue(myObject, rdr["item1"]); 

PropertyDescriptor的SetValue方法(与System.Reflection.PropertyInfo的等效方法不同)将为您进行类型转换 – 将字符串解析为int,依此类推。

对此有用的是可以想象一种属性驱动的方法来迭代该属性集合( PropertyDescriptor有一个Attributes属性,允许您获取添加到属性中的任何自定义属性)确定要使用的datareader中的哪个值; 或者有一个接收propertyname字典的方法 – columnname mappings迭代并为你执行所有这些集合。

我怀疑像这样的方法可能会以lambda-expressionreflection技巧 – 在这种情况下 – 不会这样的方式为您提供所需的API快捷方式。

我喜欢用表达式树来解决这个问题。 每当您有一个想要获取“属性委托”的方法时,请使用参数类型Expression> 。 例如:

 public void SetPropertyFromDbValue( T obj, Expression> expression, TProperty value ) { MemberExpression member = (MemberExpression)expression.Body; PropertyInfo property = (PropertyInfo)member.Member; property.SetValue(obj, value, null); } 

关于这一点的好处是,获取的语法看起来也是一样的。

 public TProperty GetPropertyFromDbValue( T obj, Expression> expression ) { MemberExpression member = (MemberExpression)expression.Body; PropertyInfo property = (PropertyInfo)member.Member; return (TProperty)property.GetValue(obj, null); } 

或者,如果你感到懒惰:

 public TProperty GetPropertyFromDbValue( T obj, Expression> expression ) { return expression.Compile()(obj); } 

调用看起来像:

 SetPropertyFromDbValue(myClass, o => o.Property1, reader["field1"]); GetPropertyFromDbValue(myClass, o => o.Property1); 

忽略这在您的具体情况下是否有用(我认为您采用的方法效果很好),您的问题是“有没有办法将属性转换为委托”。

嗯,有种可能。

实际上(幕后)每个属性都包含一个或两个方法 – set方法和/或get方法。 你可以 – 如果你能掌握这些方法 – 让代表们包装它们。

例如,一旦你掌握了一个System.Reflection.PropertyInfo对象,表示类型为TProp的类型TObj ,我们就可以创建一个Action (即一个带有一个对象的委托)在哪个设置属性和一个值来设置它)包装该setter方法如下:

 Delegate.CreateDelegate(typeof (Action), propertyInfo.GetSetMethod()) 

或者我们可以创建一个Action ,它将setter包装在特定的TObj实例上,如下所示:

 Delegate.CreateDelegate(typeof (Action), instance, propertyInfo.GetSetMethod()) 

我们可以使用静态reflection扩展方法来包装那么多:

 public static Action GetPropertySetter(this TObject instance, Expression> 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) Delegate.CreateDelegate(typeof(Action), instance, setter); } 

现在我可以在这样的对象上获取一个属性的’setter’委托:

 MyClass myObject = new MyClass(); Action setter = myObject.GetPropertySetter(o => o.Property1); 

这是强类型的,基于属性本身的类型,所以它在重构和编译时类型检查时是健壮的。

当然,在您的情况下,您希望能够使用可能为null的对象设置属性,因此围绕setter的强类型包装器不是整个解决方案 – 但它确实可以为您提供传递给SetPropertyFromDbValue方法的内容。

不,没有什么类似于属性的方法组转换。 您可以做的最好的事情是使用lambda表达式来形成Func (对于getter)或Action (对于setter):

 SetPropertyFromDbValue(value => myClass.Property1 = value, rdr["field1"]); 

值得一提的是你可以用一些反思技巧做到这一点……就像…

 public static void LoadFromReader(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(reader,"Property1","field1"); 

正如其他人所指出的那样,静态reflection是最佳选择。

这些课程开箱即用:

http://www.codeproject.com/Articles/36262/Getting-Fun-with-Net-Static-Reflection.aspx