关于c#:使用反射设置对象属性

Set object property using reflection

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

前任:

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

我想让obj.Name反射。类似:

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

有办法做到这一点吗?


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

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

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

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

1
2
3
4
5
6
7
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);
}


您还可以执行以下操作:

1
2
3
4
5
Type type = target.GetType();

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

prop.SetValue (target, propertyValue, null);

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


反射,基本上,即

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

或者有一些库可以在方便性和性能方面提供帮助;例如,对于fastmember:

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

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


或者你可以把Marc的一行包装在你自己的扩展类中:

1
2
3
4
5
6
7
public static class PropertyExtension{      

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

这样称呼它:

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

为了更好地度量,我们添加一个方法来获取属性值:

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

是的,使用System.Reflection

1
2
3
4
5
6
7
using System.Reflection;

...

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

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

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

通过反射,一切都可以是一本开放的书:)在我的示例中,我们绑定到一个私有实例级字段。


使用这样的东西:

1
2
3
4
5
6
7
8
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);
   }
}

1
2
3
4
5
6
7
8
9
10
11
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);
   }
}


如果要使用属性名称从其他对象批量分配对象的属性,可以尝试此操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;
            }
        }
    }
}


根据MarcGravell的建议,我构造了以下静态方法,该方法使用fastmember将源对象到目标的所有匹配属性一般地分配给目标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 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);
            }
        }
    }

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

这是包裹

按对象从根目录的路径设置其属性的值。

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

用途:

要直接在对象根目录下设置属性,请执行以下操作:

即,LineItem类有一个称为ItemId的int属性。

1
2
3
LineItem lineItem = new LineItem();

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

用于在对象根目录下设置多个级别的嵌套属性:

也就是说,Invite类有一个名为State的属性,它有一个名为Invite的属性(invite类型),它有一个名为Recipient的属性,它有一个名为Id的属性。

更复杂的是,State属性不是引用类型,它是struct属性。

下面是如何在对象树底部的一行中设置id属性(设置为字符串值"outlook")。

1
2
3
Invite invite = new Invite();

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