例如:
MyObject obj = new MyObject();
obj.Name = "Value";
我想使用反射来设置obj.Name
。类似于:
Reflection.SetProperty(obj, "Name") = "Value";
有没有一种方法可以做到这一点?
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是将要设置其属性的对象。
反射,基本上就是这样。
myObject.GetType().GetProperty(property).SetValue(myObject, "Bob", null);
或者有一些库可以在方便性和性能方面提供帮助;例如使用FastMember:
var wrapped = ObjectAccessor.Create(obj);
wrapped[property] = "Bob";
(这也有一个优点,就是不需要预先知道它是字段还是属性)
或者您可以将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);
}
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);
}
}
是的,可以使用 System.Reflection
:
using System.Reflection;
...
string prop = "name";
PropertyInfo pi = myObject.GetType().GetProperty(prop);
pi.SetValue(myObject, "Bob", null);
你也可以使用类似的方式访问字段:
var obj=new MyObject();
FieldInfo fi = obj.GetType().
GetField("Name", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(obj,value)
通过反射,所有东西都能暴露出来。在我的例子中,我们将绑定到一个私有的实例级别字段。
当您想使用属性名称从另一个对象批量分配对象的属性时,可以尝试以下方法:
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包,它允许在任何深度中设置给定对象的嵌套属性,而不仅仅是第一级属性。
这里是包。
通过其从根路径的路径设置对象的属性值。
对象可以是复杂对象,属性可以是多级深度嵌套属性,也可以是直接位于根下的属性。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: "_");
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);
}
}
}
var val = Convert.ChangeType(propValue, propInfo.PropertyType);
- LostNomad311obj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...)
。 - tecfieldCanWrite=False
类型的值是不可能的,对吧? - T.Todua