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

时间:2010-07-30 13:46:08

标签: c# .net vb.net c#-3.0 c#-4.0

6 个答案:

答案 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