使用反射设置对象属性


323

在C#中,有没有一种方法可以使用反射来设置对象属性?

例如:

MyObject obj = new MyObject();
obj.Name = "Value";

我想开始obj.Name思考。就像是:

Reflection.SetProperty(obj, "Name") = "Value";

有办法吗?

Answers:


391

是的,您可以使用Type.InvokeMember()

using System.Reflection;
MyObject obj = new MyObject();
obj.GetType().InvokeMember("Name",
    BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
    Type.DefaultBinder, obj, "Value");

如果obj没有名为的属性Name,或者无法设置,则会抛出异常。

另一种方法是获取属性的元数据,然后进行设置。这将允许您检查属性的存在,并验证是否可以设置:

using System.Reflection;
MyObject obj = new MyObject();
PropertyInfo prop = obj.GetType().GetProperty("Name", BindingFlags.Public | BindingFlags.Instance);
if(null != prop && prop.CanWrite)
{
    prop.SetValue(obj, "Value", null);
}

73
如果您未处理所有字符串,则可能要先转换数据:var val = Convert.ChangeType(propValue, propInfo.PropertyType); 来源:devx.com/vb2themax/Tip/19599
LostNomad311

4
或者,您可以使用obj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...)
tecfield '17

1
它不可能为CanWrite=False类型设置值,对吗?
T.Todua

287

您也可以这样做:

Type type = target.GetType();

PropertyInfo prop = type.GetProperty("propertyName");

prop.SetValue (target, propertyValue, null);

target是将设置其属性的对象。


11
我今天做了同样的事情。上面的方法效果很好,显然在尝试使用prop之前应该对prop进行null检查。
安东尼斯科特

3
@AntonyScott我想您想知道您是否调用了错误的属性,因此“静默失败”似乎是一个糟糕的过程。
jih 2014年

3
@jih我明白您的意思,但这取决于实际情况。
安东尼·斯科特

94

反映,基本上,即

myObject.GetType().GetProperty(property).SetValue(myObject, "Bob", null);

或者有一些库在便利性和性能方面都可以提供帮助;例如,使用FastMember

var wrapped = ObjectAccessor.Create(obj); 
wrapped[property] = "Bob";

(它的优点是不需要事先知道它是字段还是属性)


哇,合并让我有些困惑,但是我又找到了你的答案!谢谢,您应该得到一个“接受”,但是由于我的线程已合并:(再次感谢!
Halfpastfour.am 2012年

@MarcGravell,我当时在看FastMember,这很有趣。有什么入门/教程供我们凡人使用您的伟大图书馆吗?
Sudhanshu Mishra

如何通过FastMember获取属性的类型?
Said Roohullah Allem 2014年

@Jahan访问者=> GetMembers =>成员=>类型
Marc Gravell

好答案!有关oneliner的好处是,它的速度要快得多了解,因为没有用户命名的变量之间的这对自己可能没有任何意义..
巴斯蒂安

27

或者,您可以在自己的扩展类中包装Marc的一根衬纸:

public static class PropertyExtension{       

   public static void SetPropertyValue(this object obj, string propName, object value)
    {
        obj.GetType().GetProperty(propName).SetValue(obj, value, null);
    }
}

并这样称呼它:

myObject.SetPropertyValue("myProperty", "myValue");

为了达到良好的效果,让我们添加一个方法来获取属性值:

public static object GetPropertyValue(this object obj, string propName)
{
        return obj.GetType().GetProperty(propName).GetValue (obj, null);
}

14

是的,使用System.Reflection

using System.Reflection;

...

    string prop = "name";
    PropertyInfo pi = myObject.GetType().GetProperty(prop);
    pi.SetValue(myObject, "Bob", null);

13

使用这样的东西:

public static class PropertyExtension{       

   public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
   {
    PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
    property.SetValue(p_object, Convert.ChangeType(value, property.PropertyType), null);
   }
}

要么

public static class PropertyExtension{       

   public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
   {
    PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
    Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
    object safeValue = (value == null) ? null : Convert.ChangeType(value, t);

    property.SetValue(p_object, safeValue, null);
   }
}

2
获得属性类型然后进行转换的部分对我来说真的很有用。它像一种魅力。谢谢
Marc 2015年

12

您也可以使用类似方式访问字段:

var obj=new MyObject();
FieldInfo fi = obj.GetType().
  GetField("Name", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(obj,value)

通过反思,一切都可以成为一本打开的书:)在我的示例中,我们绑定到私有实例级别字段。


8

当您想使用属性名称从另一个对象大量分配一个对象的属性时,可以尝试一下:

public static void Assign(this object destination, object source)
    {
        if (destination is IEnumerable && source is IEnumerable)
        {
            var dest_enumerator = (destination as IEnumerable).GetEnumerator();
            var src_enumerator = (source as IEnumerable).GetEnumerator();
            while (dest_enumerator.MoveNext() && src_enumerator.MoveNext())
                dest_enumerator.Current.Assign(src_enumerator.Current);
        }
        else
        {
            var destProperties = destination.GetType().GetProperties();
            foreach (var sourceProperty in source.GetType().GetProperties())
            {
                foreach (var destProperty in destProperties)
                {
                    if (destProperty.Name == sourceProperty.Name && destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
                    {
                        destProperty.SetValue(destination,     sourceProperty.GetValue(source, new object[] { }), new object[] { });
                        break;
            }
        }
    }
}

2
嗨,欢迎来到Stack Overflow。请正确格式化您的代码,而不仅仅是将其转储到您的帖子中,这将帮助其他人理解您的答案。
ThreeFx 2014年

0

我刚刚发布了一个Nuget程序包,该程序包不仅可以设置第一级属性,还可以在给定对象中以任何深度设置嵌套属性。

这是

通过对象从根开始的路径设置其属性值。

该对象可以是复杂的对象,并且该属性可以是多层深层嵌套属性,也可以是直接在根目录下的属性。ObjectWriter将使用属性路径参数找到该属性并更新其值。属性路径是从根到我们要设置的末端节点属性访问的属性的附加名称,由定界符字符串参数定界。

用法:

要直接在对象根目录下设置属性:

就是 LineItem类具有一个称为的int属性ItemId

LineItem lineItem = new LineItem();

ObjectWriter.Set(lineItem, "ItemId", 13, delimiter: null);

要在对象根目录下多层设置嵌套属性:

就是 Invite类具有一个名为的属性State,该属性具有名为Invite(邀请类型的)属性Recipient,该属性具有的属性,具有名为的属性Id

为了使事情更加复杂,该State属性不是引用类型,而是一个struct

这是在一行中的对象树底部设置Id属性(将字符串值设置为“ outlook”)的方法。

Invite invite = new Invite();

ObjectWriter.Set(invite, "State_Invite_Recipient_Id", "outlook", delimiter: "_");

0

根据MarcGravell的建议,我构造了以下静态方法,该方法通常使用FastMember将所有匹配属性从源对象分配给目标

 public static void DynamicPropertySet(object source, object target)
    {
        //SOURCE
        var src_accessor = TypeAccessor.Create(source.GetType());
        if (src_accessor == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var src_members = src_accessor.GetMembers();
        if (src_members == null)
        {
            throw new ApplicationException("Could not fetch members!");
        }
        var src_class_members = src_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
        var src_class_propNames = src_class_members.Select(x => x.Name);
        var src_propNames = src_members.Except(src_class_members).Select(x => x.Name);

        //TARGET
        var trg_accessor = TypeAccessor.Create(target.GetType());
        if (trg_accessor == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var trg_members = trg_accessor.GetMembers();
        if (trg_members == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var trg_class_members = trg_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
        var trg_class_propNames = trg_class_members.Select(x => x.Name);
        var trg_propNames = trg_members.Except(trg_class_members).Select(x => x.Name);



        var class_propNames = trg_class_propNames.Intersect(src_class_propNames);
        var propNames = trg_propNames.Intersect(src_propNames);

        foreach (var propName in propNames)
        {
            trg_accessor[target, propName] = src_accessor[source, propName];
        }
        foreach (var member in class_propNames)
        {
            var src = src_accessor[source, member];
            var trg = trg_accessor[target, member];
            if (src != null && trg != null)
            {
                DynamicPropertySet(src, trg);
            }
        }
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.