簡體   English   中英

使用反射設置 object 屬性

[英]Set object property using reflection

C# 中是否有一種方法可以使用反射來設置 object 屬性?

前任:

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

我想用反射設置obj.Name 就像是:

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

有沒有辦法做到這一點?

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

你也可以這樣做:

Type type = target.GetType();

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

prop.SetValue (target, propertyValue, null);

其中 target 是將設置其屬性的 object。

反射,基本上,即

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

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

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

(這還有一個好處是不需要提前知道是字段還是屬性)

或者您可以將 Marc 的一個襯墊包裹在您自己的擴展 class 中:

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);
}

是的,使用System.Reflection

using System.Reflection;

...

    string prop = "name";
    PropertyInfo pi = myObject.GetType().GetProperty(prop);
    pi.SetValue(myObject, "Bob", 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);
    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);
   }
}

您還可以使用類似的方式訪問字段:

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

通過反射,一切都可以成為一本打開的書:) 在我的示例中,我們綁定到一個私有實例級字段。

當您想使用屬性名稱從另一個 Object 批量分配 Object 的屬性時,可以試試這個:

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;
            }
        }
    }
}

我剛剛發布了 Nuget package,它不僅允許設置第一級屬性,還允許在給定的 object 中設置任何深度的嵌套屬性。

這是package

通過從根開始的路徑設置 object 的屬性值。

object 可以是一個復雜的 object,該屬性可以是多級深度嵌套屬性,也可以是根目錄下的屬性。 ObjectWriter將使用屬性路徑參數查找屬性並更新其值。 屬性路徑是從根到我們要設置的結束節點屬性訪問的屬性的附加名稱,由分隔符字符串參數分隔。

用法:

要直接在 object 根目錄下設置屬性:

IE。 LineItem class 有一個名為ItemId的 int 屬性

LineItem lineItem = new LineItem();

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

要在 object 根下設置嵌套屬性多個級別:

IE。 Invite class 有一個名為State的屬性,它有一個名為Invite (Invite 類型)的屬性,它有一個名為Recipient的屬性,它有一個名為Id的屬性。

更復雜的是, State屬性不是引用類型,而是struct

以下是如何在一行中設置 object 樹底部的 Id 屬性(“outlook”的字符串值)。

Invite invite = new Invite();

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

根據 MarcGravell 的建議,我構建了以下 static 方法。該方法通常使用FastMember將源 object 中的所有匹配屬性分配給目標

 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);
            }
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM