2009-03-06 1 views
243

Есть ли способ в C# 3.5, где я могу использовать отражение для установки свойства объекта?Установить свойство объекта с помощью отражения

Ex:

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

Я хочу установить obj.Name с отражением. Что-то вроде:

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

Есть ли способ сделать это?

ответ

297

Да, вы можете использовать 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); 
} 
+55

Если вы не имеете дело со всеми строками вы можете сначала конвертировать данные: var var = Convert.ChangeType (propValue, propInfo.PropertyType); ' источник: http://www.devx.com/vb2themax/Tip/19599 – LostNomad311

+2

альтернативно, лет u может использовать 'obj.GetType(). GetProperty (« Name ») ?. GetSetMethod() ?. Invoke (...)' – tecfield

227

Вы также можете сделать:

Type type = target.GetType(); 

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

prop.SetValue (target, propertyValue, null); 

, где целью является объект, который будет иметь свою собственность задавать.

+10

Сегодня я сделал это очень точно. Вышеизложенное отлично работает, очевидно, необходимо проверить нуль на prop, прежде чем пытаться его использовать. –

+4

+1 Для более чистого кода !!! – Sham

+3

@AntonyScott Я бы подумал, что вы хотите знать, ссылаетесь ли вы на неправильное свойство, поэтому «терпеть неудачу» кажется неудачным. –

11

Вы также можете получить доступ поля, используя способ предложений этого типа:

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

С отражением все может быть открытой книгой :) В моем примере мы связывание с частным полем уровня экземпляра.

76

Отражение, в основном, т.е.

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

или есть библиотеки, которые помогут с точки зрения удобства и производительности; например, с FastMember:

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

(который также имеет преимущество, что не нужно знать заранее, будет ли это поле против собственности)

+0

Ничего себе, немного смутился от слияния, но я снова нашел ваш ответ! Спасибо, вы заслуживаете «принять», но так как моя нить объединяется :(Спасибо еще раз! –

+0

@Bob извините за любую путаницу –

+0

+1 для однострочного решения. Работал как шарм. Спасибо Marc. – pordi

13

Да, с помощью System.Reflection:

using System.Reflection; 

... 

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

Или вы можете надеть один лайнер 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); 
} 
7

Вы можете попробовать это, когда вы хотите, чтобы массы-правопреемник свойств одного Объект из другого объекта, используя имена свойств:

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

Привет и добро пожаловать в переполнение стека. правильно отформатируйте свой код и не просто сбрасывайте его в своем сообщении, это поможет другим понять ваш ответ. – ThreeFx

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

или

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

Часть, в которой вы получаете тип собственности, а затем бросаете ее, была действительно полезна для меня. Отлично работает. спасибо – Marc

0

Я только что опубликовал пакет NuGet, что позволяет настраивать не только первые свойства уровня, но и вложенные свойства в данном объекте в любой глубине.

Здесь the package

Устанавливает значение свойства объекта по его пути от корня.

Объект может быть сложным объектом, а свойство может быть многоуровневым вложенным свойством или может быть свойством непосредственно под корнем. ObjectWriter найдет свойство, используя параметр пути свойства и обновит его значение. Путь свойств - это добавленные имена свойств, которые были посещены от корня до свойства конечного узла, который мы хотим установить, ограниченным параметром строки разделителя.

Использование:

Для настройки свойств непосредственно в корневом каталоге объекта:

Ie. LineItem класс имеет свойство, называемое Int ItemId

LineItem lineItem = new LineItem(); 

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

для создания нескольких уровней вложенных свойств ниже корневого объекта:

Ie. Invite класс имеет свойство State, которое имеет свойство Invite (типа приглашения), которое имеет свойство Recipient, которое имеет свойство Id.

Чтобы сделать вещи еще более сложными, свойство State не является ссылочным типом, это struct.

Вот как вы можете установить свойство Id (в строковое значение «outlook») в нижней части дерева объектов в одной строке.

Invite invite = new Invite(); 

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

на основе предположения MarcGravell, я построил следующий статический метод method.The обобщенно присваивает все соответствующие свойства из исходного объекта в целевых помощью 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); 
      } 
     } 
    }