使用反射设置对象属性

381
有没有一种在C#中使用反射来设置对象属性的方法?
例如:
MyObject obj = new MyObject();
obj.Name = "Value";

我想使用反射来设置obj.Name。类似于:

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

有没有一种方法可以做到这一点?

10个回答

471

是的,你可以使用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);
}

84
如果您没有处理所有字符串,您可能需要先转换数据:var val = Convert.ChangeType(propValue, propInfo.PropertyType); - LostNomad311
8
或者,您可以使用 obj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...) - tecfield
3
设置 CanWrite=False 类型的值是不可能的,对吧? - T.Todua

332

你也可以这样做:

Type type = target.GetType();

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

prop.SetValue (target, propertyValue, null);

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


13
今天我也做了同样的事情。上述方法很好,显然在使用 prop 之前应该进行空值检查。 - Antony Scott
4
我认为你应该知道是否调用了错误的属性,所以“默默失败”似乎是不好的做法。 - j.i.h.
3
我能理解您的观点,但实际上这取决于具体情况。 - Antony Scott

105

反射,基本上就是这样。

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

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

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

(这也有一个优点,就是不需要预先知道它是字段还是属性)


哇,合并让我有点困惑,但我又找到了你的答案!谢谢你,你应该得到一个“采纳”,但因为我的帖子被合并了:( 再次感谢! - halfpastfour.am
@MarcGravell,我正在研究FastMember,它非常有趣。是否有适合我们这些凡人使用你这个伟大库的入门/教程? - Sudhanshu Mishra
如何通过FastMember获取属性的类型? - x19
@Jahan 访问器 => GetMembers => Member => Type - Marc Gravell
好答案!一行代码的好处在于它更容易理解,因为其中没有用户命名的变量,这对你自己来说可能毫无意义。 - Bastiaan
这是最直接的方法。 - undefined

32

或者您可以将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);
}

16
使用类似以下的方式:
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);
   }
}

3
获取属性类型并将其转换的部分对我非常有用。它完美地起作用。谢谢你。 - Marc

15

是的,可以使用 System.Reflection

using System.Reflection;

...

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

13

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

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

通过反射,所有东西都能暴露出来。在我的例子中,我们将绑定到一个私有的实例级别字段。


10

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

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

0

我刚刚发布了一个Nuget包,它允许在任何深度中设置给定对象的嵌套属性,而不仅仅是第一级属性。

这里是

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

对象可以是复杂对象,属性可以是多级深度嵌套属性,也可以是直接位于根下的属性。ObjectWriter将使用属性路径参数查找属性并更新其值。属性路径是从根到要设置的最终节点属性所访问的属性的附加名称,由分隔符字符串参数分隔。

用法:

用于设置直接位于对象根下的属性:

例如:LineItem类有一个名为ItemId的int属性

LineItem lineItem = new LineItem();

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

针对在对象根下设置多层嵌套属性:

例如,Invite类有一个名为State的属性,该属性具有名为Invite(类型为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);
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接