[英]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.