Получить значение свойства из строки, используя отражение в C#


625

Я пытаюсь реализовать Data transformation using Reflection пример в моем коде.

Функция GetSourceValue имеет переключатель, сравнивающий различные типы, но я хочу удалить эти типы и свойства и получить GetSourceValue значение свойства, используя только одну строку в качестве параметра. Я хочу передать класс и свойство в строке и разрешить значение свойства.

Возможно ли это?

Web Archive version of original blog post

1,269
public static object GetPropValue(object src, string propName) 
{ 
    return src.GetType().GetProperty(propName).GetValue(src, null); 
} 

Конечно, вы хотите добавить проверку и этажерку, но это суть его.

  0

Да я знаю, я могу использовать, как вы говорите. Но я не хочу передать src-объект. Я хочу передать только строку с именем типа Class1.Prop1 и дать мне значение Prop1 класса Class1. 29 июл. 092009-07-29 10:56:36

+91

Вы задали в своем вопросе «Я хочу пройти класс», я предполагаю, что это означало «объект», а не «класс», поскольку «класс» имеет мало смысла. Почему вы проголосуете за это? Можете ли вы не понять, как изменить этот код, чтобы вместо этого использовать «это»? Является ли это статическим свойством класса? Тогда вам нужно быть более конкретным, это правильный ответ. 31 июл. 092009-07-31 22:12:32

+3

src.GetType(). GetProperty (propName) .GetValue (src, null) возвращает null для вложенных объектов. Я имею в виду как School.Employee.Name в этом случае school.GetType(). GetProperty (Employee.Name) .GetValue (школа, null) 07 май. 132013-05-07 10:26:02

+7

@Murali: Потому что это неправильно. Сначала вам нужно сначала получить значение свойства «Сотрудник», а затем сделать то же самое еще раз. 07 май. 132013-05-07 17:17:10

+1

@EdS. Я работаю над требованием, когда мне нужно переместить часть значения (свойство) на другой объект того же типа. Я также получаю список строк, содержащий имя свойства, мне нужно скопировать значение. Ex «Name», «Employee.Name» и т. Д. Мне нужно сделать отражение, чтобы получить это значение свойства от ObjA и установить его в ObjB. Есть ли способ, которым я могу справиться с этим? До сих пор я написал общий метод для получения значения свойства для данного имени свойства. Код точно ниже кода из jheddings answer.All мне нужно, как я могу установить другой объект (тот же тип, ObjB) с новым значением из ObjA? Любая идея или помощь? 08 май. 132013-05-08 12:09:29

  0

@EdS. Он [не работает] (https://dotnetfiddle.net/Widget/lLPfdD). Что мне не хватает? 06 ноя. 142014-11-06 09:20:28

  0

@ TomášZato: Это не работает, потому что 'test' не является свойством, это поле. Используйте свойство или используйте правильный вызов отражения, чтобы получить значение поля. Просто любопытно; вы ответили на этот ответ? Если это так, возможно, вам следует пересмотреть. 06 ноя. 142014-11-06 20:00:18

  0

@EdS. Да, извините. Я совершенно не знал, что в C# есть что-то, называемое ['поле'] (http://msdn.microsoft.com/en-US/library/ms173118%28v=vs.80%29.aspx), а не свойство. Мое голосование заблокировано. Что я могу сделать? 06 ноя. 142014-11-06 22:06:57

+8

@ TomášZato: Все в порядке, downvote не biggie, просто убедитесь, что вы знаете, о чем говорите, прежде чем раздавать downvotes в будущем. 07 ноя. 142014-11-07 02:22:44

+4

Ницца и просто! Я бы сделал его общим: 'public static T GetPropertyValue <T> (object obj, string propName) {return (T) obj.GetType(). GetProperty (propName).GetValue (obj, null); } ' 30 май. 152015-05-30 10:29:31

  0

Вы должны включить пример, неясно, что должен содержать src. 05 янв. 172017-01-05 02:42:30

  0

@rolls: А? 'src' - это объект ...' GetType' определен на объекте ... 'src' - это то, что вы хотите. 05 янв. 172017-01-05 19:48:16

  0

Извините, я понимаю, как его использовать. Я просто подумал, что это может быть более аккуратно, если вы показали еще несколько строк с примером того, как использовать эту функцию, поскольку она может быть не сразу понятна. 06 янв. 172017-01-06 00:24:37

  0

@rolls, вы можете буквально сделать это на любом объекте любого типа. 15 фев. 172017-02-15 16:50:14

  0

Блестящий! именно то, что я искал. Спасибо! 06 апр. 172017-04-06 13:52:57


166

Как о чем-то вроде этого:

public static Object GetPropValue(this Object obj, String name) { 
    foreach (String part in name.Split('.')) { 
     if (obj == null) { return null; } 

     Type type = obj.GetType(); 
     PropertyInfo info = type.GetProperty(part); 
     if (info == null) { return null; } 

     obj = info.GetValue(obj, null); 
    } 
    return obj; 
} 

public static T GetPropValue<T>(this Object obj, String name) { 
    Object retval = GetPropValue(obj, name); 
    if (retval == null) { return default(T); } 

    // throws InvalidCastException if types are incompatible 
    return (T) retval; 
} 

Это позволит вам спуститься в свойства, используя одну строку, как это:

DateTime now = DateTime.Now; 
int min = GetPropValue<int>(now, "TimeOfDay.Minutes"); 
int hrs = now.GetPropValue<int>("TimeOfDay.Hours"); 

Вы можете использовать эти методы как статические методы или расширения.

+2

@FredJand рад, что ты наткнулся на него! Это всегда удивительно, когда появляются эти старые сообщения. Это было немного расплывчато, поэтому я добавил немного текста, чтобы объяснить это. Я также переключился на использование этих методов расширения и добавил форму дженериков, поэтому я добавил ее здесь. 08 ноя. 122012-11-08 15:23:31

  0

Почему нулевой охранник в foreach и не выше? 17 май. 162016-05-17 11:03:57

+2

@Santhos, поскольку «obj» переопределяется в теле цикла foreach, он проверяется во время каждой итерации. 21 июн. 162016-06-21 16:13:56

  0

Очевидно ... Я только слепой, извините и спасибо! 22 июн. 162016-06-22 09:18:37


2

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

using System.Reflection; 
public object GetPropValue(string prop) 
{ 
    int splitPoint = prop.LastIndexOf('.'); 
    Type type = Assembly.GetEntryAssembly().GetType(prop.Substring(0, splitPoint)); 
    object obj = null; 
    return type.GetProperty(prop.Substring(splitPoint + 1)).GetValue(obj, null); 
} 

Обратите внимание, что я отметил объект, который подвергается осмотру с локальной переменной obj. null означает статичный, в противном случае установите его на то, что вы хотите. Также обратите внимание, что GetEntryAssembly() является одним из нескольких доступных способов получения «бегущей» сборки, вы можете захотеть поиграть с ней, если вам тяжело загружать этот тип.


38

Как насчет использования CallByName пространства имен Microsoft.VisualBasic (Microsoft.VisualBasic.dll)? Он использует отражение для получения свойств, полей и методов обычных объектов, объектов COM и даже динамических объектов.

using Microsoft.VisualBasic; 
using Microsoft.VisualBasic.CompilerServices; 

, а затем

Versioned.CallByName(this, "method/function/prop name", CallType.Get).ToString(); 
+4

Интересное предложение, дальнейшая проверка показала, что он может обрабатывать как поля, так и свойства, COM-объекты ** и может даже обрабатывать корректно динамическое связывание **! 02 ноя. 142014-11-02 17:31:22


1
Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable, "YourVariableName", CallType), YourType) 

22

Великий ответ на jheddings. Я хотел бы улучшить это позволяет реферирование агрегированных массивов или коллекций объектов, так что ИмениСвойства может быть property1.property2 [X] .property3:

public static object GetPropertyValue(object srcobj, string propertyName) 
    { 
     if (srcobj == null) 
      return null; 

     object obj = srcobj; 

     // Split property name to parts (propertyName could be hierarchical, like obj.subobj.subobj.property 
     string[] propertyNameParts = propertyName.Split('.'); 

     foreach (string propertyNamePart in propertyNameParts) 
     { 
      if (obj == null) return null; 

      // propertyNamePart could contain reference to specific 
      // element (by index) inside a collection 
      if (!propertyNamePart.Contains("[")) 
      { 
       PropertyInfo pi = obj.GetType().GetProperty(propertyNamePart); 
       if (pi == null) return null; 
       obj = pi.GetValue(obj, null); 
      } 
      else 
      { // propertyNamePart is areference to specific element 
       // (by index) inside a collection 
       // like AggregatedCollection[123] 
       // get collection name and element index 
       int indexStart = propertyNamePart.IndexOf("[")+1; 
       string collectionPropertyName = propertyNamePart.Substring(0, indexStart-1); 
       int collectionElementIndex = Int32.Parse(propertyNamePart.Substring(indexStart, propertyNamePart.Length-indexStart-1)); 
       // get collection object 
       PropertyInfo pi = obj.GetType().GetProperty(collectionPropertyName); 
       if (pi == null) return null; 
       object unknownCollection = pi.GetValue(obj, null); 
       // try to process the collection as array 
       if (unknownCollection.GetType().IsArray) 
       { 
        object[] collectionAsArray = unknownCollection as Array[]; 
        obj = collectionAsArray[collectionElementIndex]; 
       } 
       else 
       { 
        // try to process the collection as IList 
        System.Collections.IList collectionAsList = unknownCollection as System.Collections.IList; 
        if (collectionAsList != null) 
        { 
         obj = collectionAsList[collectionElementIndex]; 
        } 
        else 
        { 
         // ??? Unsupported collection type 
        } 
       } 
      } 
     } 

     return obj; 
    } 
  0

как насчет списка списков, доступных через MasterList [0] [1]? 24 июн. 152015-06-24 14:43:18


4

Использование PropertyInfo в System.Reflection имен. Отражение компилируется просто отлично, независимо от того, какое свойство мы пытаемся получить. Ошибка будет появляться во время выполнения.

public static object GetObjProperty(object obj, string property) 
    { 
     Type t = obj.GetType(); 
     PropertyInfo p = t.GetProperty("Location"); 
     Point location = (Point)p.GetValue(obj, null); 
     return location; 
    } 

Он отлично работает, чтобы получить расположение свойство объекта

Label1.Text = GetObjProperty(button1, "Location").ToString(); 

Мы получим Расположение: {X = 71, Y = 27} Мы также можем вернуться location.X или location.Y на том же пути.


1

короткий путь ....

var a = new Test { Id = 1 , Name = "A" , date = DateTime.Now}; 
var b = new Test { Id = 1 , Name = "AXXX", date = DateTime.Now }; 

var compare = string.Join("",a.GetType().GetProperties().Select(x => x.GetValue(a)).ToArray())== 
       string.Join("",b.GetType().GetProperties().Select(x => x.GetValue(b)).ToArray()); 

37

Добавить в любой Class:

public class Foo 
{ 
    public object this[string propertyName] 
    { 
     get { return this.GetType().GetProperty(propertyName).GetValue(this, null); } 
     set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); } 
    } 

    public string Bar { get; set; } 
} 

Затем вы можете использовать как:

Foo f = new Foo(); 
// Set 
f["Bar"] = "asdf"; 
// Get 
string s = (string)f["Bar"]; 
+5

Это довольно аккуратно - делает это PHP-esque. 11 сен. 142014-09-11 22:45:15

  0

@EduardoCuomo: Можно ли использовать отражение с этим, чтобы вам не нужно было знать, какие члены класса имеют? 20 авг. 152015-08-20 21:44:33

  0

Можно ли это сделать, если «Бар» был объектом? 08 июл. 172017-07-08 17:49:44

+1

Сделайте объект похожим на словарь со строковым ключом !!! awesome 20 авг. 172017-08-20 07:08:13

  0

@big_water методы 'SetValue' и' GetValue' работают с 'Object'. Если вам нужно работать с определенным типом, вы должны указать результат «GetValue» и указать значение, чтобы присвоить его «SetValue» 03 ноя. 172017-11-03 13:18:59

  0

Извините @OurManinBananas, я не могу понять ваш вопрос. Что ты хочешь делать? 03 ноя. 172017-11-03 13:20:12

  0

Что такое имя этого типа методов ..? 03 янв. 182018-01-03 14:19:02


2

Вот еще один способ найти вложенное свойство, которое не требует, чтобы строка указывала вам путь вложенности. Приобретите Ed S. для метода одиночных свойств.

public static T FindNestedPropertyValue<T, N>(N model, string propName) { 
     T retVal = default(T); 
     bool found = false; 

     PropertyInfo[] properties = typeof(N).GetProperties(); 

     foreach (PropertyInfo property in properties) { 
      var currentProperty = property.GetValue(model, null); 

      if (!found) { 
       try { 
        retVal = GetPropValue<T>(currentProperty, propName); 
        found = true; 
       } catch { } 
      } 
     } 

     if (!found) { 
      throw new Exception("Unable to find property: " + propName); 
     } 

     return retVal; 
    } 

     public static T GetPropValue<T>(object srcObject, string propName) { 
     return (T)srcObject.GetType().GetProperty(propName).GetValue(srcObject, null); 
    } 
  0

Может быть, лучше проверить, является ли ['Type.GetProperty'] (https://msdn.microsoft.com/en-us/library/kz0a8sxy (v = vs.110) .aspx) возвращает' null' вместо вызова 'GetValue' и с вызовом NullReferenceException в цикле. 11 фев. 162016-02-11 08:22:13


2

Следующий код представляет собой рекурсивный метод для отображения всей иерархии всех имен и значений свойств, содержащихся в случае объекта. Этот метод использует упрощенную версию ответа AlexD's GetPropertyValue() выше в этом потоке. Благодаря этой дискуссионной теме я смог выяснить, как это сделать!

Например, я использую этот метод, чтобы показать взрыв или дамп всех свойств в WebService ответ с помощью вызова метода следующим образом:

PropertyValues_byRecursion("Response", response, false);

public static object GetPropertyValue(object srcObj, string propertyName) 
{ 
    if (srcObj == null) 
    { 
    return null; 
    } 
    PropertyInfo pi = srcObj.GetType().GetProperty(propertyName.Replace("[]", "")); 
    if (pi == null) 
    { 
    return null; 
    } 
    return pi.GetValue(srcObj); 
} 

public static void PropertyValues_byRecursion(string parentPath, object parentObj, bool showNullValues) 
{ 
    /// Processes all of the objects contained in the parent object. 
    /// If an object has a Property Value, then the value is written to the Console 
    /// Else if the object is a container, then this method is called recursively 
    ///  using the current path and current object as parameters 

    // Note: If you do not want to see null values, set showNullValues = false 

    foreach (PropertyInfo pi in parentObj.GetType().GetTypeInfo().GetProperties()) 
    { 
    // Build the current object property's namespace path. 
    // Recursion extends this to be the property's full namespace path. 
    string currentPath = parentPath + "." + pi.Name; 

    // Get the selected property's value as an object 
    object myPropertyValue = GetPropertyValue(parentObj, pi.Name); 
    if (myPropertyValue == null) 
    { 
     // Instance of Property does not exist 
     if (showNullValues) 
     { 
     Console.WriteLine(currentPath + " = null"); 
     // Note: If you are replacing these Console.Write... methods callback methods, 
     //  consider passing DBNull.Value instead of null in any method object parameters. 
     } 
    } 
    else if (myPropertyValue.GetType().IsArray) 
    { 
     // myPropertyValue is an object instance of an Array of business objects. 
     // Initialize an array index variable so we can show NamespacePath[idx] in the results. 
     int idx = 0; 
     foreach (object business in (Array)myPropertyValue) 
     { 
     if (business == null) 
     { 
      // Instance of Property does not exist 
      // Not sure if this is possible in this context. 
      if (showNullValues) 
      { 
      Console.WriteLine(currentPath + "[" + idx.ToString() + "]" + " = null"); 
      } 
     } 
     else if (business.GetType().IsArray) 
     { 
      // myPropertyValue[idx] is another Array! 
      // Let recursion process it. 
      PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues); 
     } 
     else if (business.GetType().IsSealed) 
     { 
      // Display the Full Property Path and its Value 
      Console.WriteLine(currentPath + "[" + idx.ToString() + "] = " + business.ToString()); 
     } 
     else 
     { 
      // Unsealed Type Properties can contain child objects. 
      // Recurse into my property value object to process its properties and child objects. 
      PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues); 
     } 
     idx++; 
     } 
    } 
    else if (myPropertyValue.GetType().IsSealed) 
    { 
     // myPropertyValue is a simple value 
     Console.WriteLine(currentPath + " = " + myPropertyValue.ToString()); 
    } 
    else 
    { 
     // Unsealed Type Properties can contain child objects. 
     // Recurse into my property value object to process its properties and child objects. 
     PropertyValues_byRecursion(currentPath, myPropertyValue, showNullValues); 
    } 
    } 
} 

5

О в обсуждении вложенных свойств вы можете избежать всех отражений, если используете DataBinder.Eval Method (Object, String), как показано ниже:

var value = DataBinder.Eval(DateTime.Now, "TimeOfDay.Hours"); 

Конечно, вам нужно будет добавить ссылку на сборку System.Web, но это, вероятно, не имеет большого значения.


3
public static List<KeyValuePair<string, string>> GetProperties(object item) //where T : class 
    { 
     var result = new List<KeyValuePair<string, string>>(); 
     if (item != null) 
     { 
      var type = item.GetType(); 
      var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); 
      foreach (var pi in properties) 
      { 
       var selfValue = type.GetProperty(pi.Name).GetValue(item, null); 
       if (selfValue != null) 
       { 
        result.Add(new KeyValuePair<string, string>(pi.Name, selfValue.ToString())); 
       } 
       else 
       { 
        result.Add(new KeyValuePair<string, string>(pi.Name, null)); 
       } 
      } 
     } 
     return result; 
    } 

Это способ получить все свойства со своими значениями в списке.

  0

Зачем это делать: 'type.GetProperty (pi.Name)', если это == переменной 'pi'? 06 янв. 162016-01-06 13:49:27

  0

Если вы используете C# 6.0, избавитесь от 'if' и выполните' selfValue? .ToString() 'В противном случае избавиться от' if' и использовать 'selfValue == null? Null: selfValue.ToString()' 06 янв. 162016-01-06 13:50:16

  0

Также a список 'List <KeyValuePair <' нечетный, используйте словарь 'Dictionary <string, string>' 06 янв. 162016-01-06 13:53:53


1

jheddings и AlexD оба написаны отличные ответы о том, как разрешать строки свойств. Я хотел бы бросить шахту в микс, так как я написал специальную библиотеку именно для этой цели.

Pather.CSharp основной класс: Resolver. По умолчанию он может разрешать записи свойств, массива и словаря.

Так, например, если у вас есть объект, как этот

var o = new { Property1 = new { Property2 = "value" } }; 

и хотите получить Property2, вы можете сделать это следующим образом:

IResolver resolver = new Resolver(); 
var path = "Property1.Property2"; 
object result = r.Resolve(o, path); 
//=> "value" 

Это самый простой пример пути, которые он может решить.Если вы хотите увидеть, что еще он может, или как вы можете продлить его, просто отправляйтесь в Github page.


6

Если я использую код из Ed S. я получаю

«ReflectionExtensions.GetProperty (тип, строка)» недоступен из-за его уровня защиты

Кажется, что GetProperty() не доступен в Xamarin.Forms. TargetFrameworkProfile - это Profile7 в моей библиотеке портативных классов (.NET Framework 4.5, Windows 8, ASP.NET Core 1.0, Xamarin.Android, Xamarin.iOS, Xamarin.iOS Classic).

Теперь я нашел рабочий раствор:

using System.Linq; 
using System.Reflection; 

public static object GetPropValue(object source, string propertyName) 
{ 
    var property = source.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparison.OrdinalIgnoreCase)); 
    if(property != null) 
    { 
     return property.GetValue(source); 
    } 
    return null; 
} 

Source

  0

Просто небольшое возможное улучшение. Заменить IF и следующий возврат: return property? .GetValue (источник); 08 фев. 172017-02-08 11:31:57


3

Метод, который был изменен в .NET Standard (как 1,6). Также мы можем использовать нулевой условный оператор C# 6.

using System.Reflection; 
public static object GetPropValue(object src, string propName) 
{ 
    return src.GetType().GetRuntimeProperty(propName)?.GetValue(src); 
} 

1
public static TValue GetFieldValue<TValue>(this object instance, string name) 
{ 
    var type = instance.GetType(); 
    var field = type.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.FieldType) && e.Name == name); 
    return (TValue)field?.GetValue(instance); 
} 

public static TValue GetPropertyValue<TValue>(this object instance, string name) 
{ 
    var type = instance.GetType(); 
    var field = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.PropertyType) && e.Name == name); 
    return (TValue)field?.GetValue(instance); 
} 

0

Вот мое решение. Он также работает с объектами COM и позволяет получать доступ к элементам коллекции/массива из COM-объектов.

public static object GetPropValue(this object obj, string name) 
{ 
    foreach (string part in name.Split('.')) 
    { 
     if (obj == null) { return null; } 

     Type type = obj.GetType(); 
     if (type.Name == "__ComObject") 
     { 
      if (part.Contains('[')) 
      { 
       string partWithoundIndex = part; 
       int index = ParseIndexFromPropertyName(ref partWithoundIndex); 
       obj = Versioned.CallByName(obj, partWithoundIndex, CallType.Get, index); 
      } 
      else 
      { 
       obj = Versioned.CallByName(obj, part, CallType.Get); 
      } 
     } 
     else 
     { 
      PropertyInfo info = type.GetProperty(part); 
      if (info == null) { return null; } 
      obj = info.GetValue(obj, null); 
     } 
    } 
    return obj; 
} 

private static int ParseIndexFromPropertyName(ref string name) 
{ 
    int index = -1; 
    int s = name.IndexOf('[') + 1; 
    int e = name.IndexOf(']'); 
    if (e < s) 
    { 
     throw new ArgumentException(); 
    } 
    string tmp = name.Substring(s, e - s); 
    index = Convert.ToInt32(tmp); 
    name = name.Substring(0, s - 1); 
    return index; 
} 

0
public class YourClass 
{ 
    //Add below line in your class 
    public object this[string propertyName] => GetType().GetProperty(propertyName)?.GetValue(this, null); 
    public string SampleProperty { get; set; } 
} 

//And you can get value of any property like this. 
var value = YourClass["SampleProperty"];