Установить свойство объекта с помощью отражения
Есть ли способ в С#, где я могу использовать отражение, чтобы установить свойство объекта?
Пример:
MyObject obj = new MyObject();
obj.Name = "Value";
Я хочу установить obj.Name
с отражением. Что-то вроде:
Reflection.SetProperty(obj, "Name") = "Value";
Есть ли способ сделать это?
Ответы
Ответ 1
Да, вы можете использовать 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);
}
Ответ 2
Вы также можете сделать:
Type type = target.GetType();
PropertyInfo prop = type.GetProperty("propertyName");
prop.SetValue (target, propertyValue, null);
где target - объект, который будет иметь свой набор свойств.
Ответ 3
Отражение, в основном, т.е.
myObject.GetType().GetProperty(property).SetValue(myObject, "Bob", null);
или есть библиотеки, которые помогут как с точки зрения удобства, так и производительности; например FastMember:
var wrapped = ObjectAccessor.Create(obj);
wrapped[property] = "Bob";
(что также имеет то преимущество, что не нужно заранее знать, является ли это полем по отношению к свойству)
Ответ 4
Или вы можете перенести 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);
}
Ответ 5
Да, используя System.Reflection
:
using System.Reflection;
...
string prop = "name";
PropertyInfo pi = myObject.GetType().GetProperty(prop);
pi.SetValue(myObject, "Bob", null);
Ответ 6
Вы также можете получить доступ к полям с помощью simillar:
var obj=new MyObject();
FieldInfo fi = obj.GetType().
GetField("Name", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(obj,value)
С отражением все может быть открытой книгой:) В моем примере мы привязываемся к полю частного уровня экземпляра.
Ответ 7
Используйте такие вещи, как это:
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);
}
}
Ответ 8
Вы можете попробовать это, если хотите назначить свойства объекта из другого объекта с помощью имен свойств:
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;
}
}
}
}
Ответ 9
Я только что опубликовал пакет Nuget, который позволяет настраивать не только свойства первого уровня, но и вложенные свойства в заданном объекте на любой глубине.
Вот пакет
Устанавливает значение свойства объекта по его пути из корня.
Объект может быть сложным объектом, а свойство может быть многоуровневым вложенным свойством или может быть свойством непосредственно под корнем. ObjectWriter
найдет свойство, используя параметр пути свойства и обновит его значение. Путь к свойствам - это добавленные имена свойств, посещенных от корневого до конца свойства node, который мы хотим установить, ограниченным параметром строки разделителя.
Использование:
Для настройки свойств непосредственно под корнем объекта:
Т.е. LineItem
класс имеет свойство int, называемое ItemId
LineItem lineItem = new LineItem();
ObjectWriter.Set(lineItem, "ItemId", 13, delimiter: null);
Для установки вложенного свойства несколько уровней ниже корня объекта:
Т.е. Invite
class имеет свойство State
, которое имеет свойство Invite
(типа приглашения), которое имеет свойство Recipient
, которое имеет свойство Id
.
Чтобы сделать вещи еще более сложными, свойство State
не является ссылочным типом, это struct
.
Вот как вы можете установить свойство Id (в строковое значение "outlook" ) внизу дерева объектов в одной строке.
Invite invite = new Invite();
ObjectWriter.Set(invite, "State_Invite_Recipient_Id", "outlook", delimiter: "_");
Ответ 10
Основываясь на предложении 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);
}
}
}