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

667

Я пытаюсь реализовать Преобразование данных с использованием Reflection 1 в моем коде.

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

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

1Версия веб-архива исходного сообщения в блоге

Теги:
reflection

21 ответ

1331
Лучший ответ
 public static object GetPropValue(object src, string propName)
 {
     return src.GetType().GetProperty(propName).GetValue(src, null);
 }

Конечно, вы захотите добавить валидацию и еще что-то, но это и есть ее суть.

  • 0
    Да, я знаю, что могу использовать, как вы говорите. Но я не хочу передавать объект src. Я хочу передать только строку с именем типа "Class1.Prop1" и дать мне значение Prop1 класса Class1.
  • 101
    Вы спросили в своем вопросе «Я хочу пройти класс», я предполагаю, что означает «объект», а не «класс», поскольку «класс» не имеет большого смысла. Почему вы бы проголосовали за это? Разве вы не можете понять, как изменить этот код, чтобы вместо него использовать «это»? Это статическое свойство класса? Вам нужно быть более конкретным, это правильный ответ.
Показать ещё 14 комментариев
184

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

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
    @FredJ, и рад, что ты наткнулся на это! Всегда удивительно, когда появляются эти старые посты. Это было немного расплывчато, поэтому я добавил немного текста, чтобы объяснить это. Я также перешел на использование их в качестве методов расширения и добавил форму обобщения, поэтому я добавил ее здесь.
  • 0
    Почему нулевой охранник в foreach, а не выше?
Показать ещё 1 комментарий
47

Добавить в любой 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"];
  • 0
    @EduardoCuomo: Возможно ли использовать рефлексию с этим, чтобы вам не нужно было знать, какие члены есть в классе?
  • 0
    Возможно ли это сделать, если бы «Бар» был объектом?
Показать ещё 4 комментария
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();
  • 5
    Интересное предложение, дальнейшая проверка показала, что он может обрабатывать как поля и свойства, так и COM-объекты, и даже правильно обрабатывать динамическое связывание !
  • 0
    это должен быть правильный ответ для всех :)
22

Отличный ответ на хеддинги. Я хотел бы улучшить его, позволяя ссылаться на агрегированные массивы или коллекции объектов, так что propertyName может быть 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]?
8

Если я использую код из 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));
    return property?.GetValue(source);
}

Источник

  • 4
    Просто крошечное возможное улучшение. Замените IF и следующий возврат на: return property? .GetValue (source);
6

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

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

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

4

Метод вызова изменился в .NET Standard (начиная с версии 1.6). Также мы можем использовать оператор условного оператора С# 6.

using System.Reflection; 
public static object GetPropValue(object src, string propName)
{
    return src.GetType().GetRuntimeProperty(propName)?.GetValue(src);
}
  • 1
    использовать ? operator
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;
    }

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

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

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

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 ?
  • 0
    Если вы используете c # 6.0, избавьтесь от if и сделайте selfValue?.ToString() В противном случае избавьтесь от if и используйте selfValue==null?null:selfValue.ToString()
Показать ещё 1 комментарий
2
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"];
2

Следующий код - это рекурсивный метод для отображения всей иерархии всех Имен. Имен и Значений, содержащихся в экземпляре объекта. Этот метод использует упрощенную версию AlexD 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 namespace path.  
    // Recursion extends this to be the property full namespace path.
    string currentPath = parentPath + "." + pi.Name;

    // Get the selected property 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);
    }
  }
}
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 null вместо вызова GetValue и NullReferenceException в цикле.
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() является одним из нескольких доступных способов получения "запущенной" сборки, вы можете захотеть поиграть с ним, если вам тяжело загружать этот тип.

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

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());
1
Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable, "YourVariableName", CallType), YourType)
0

Посмотрите библиотеку Heleonix.Reflection. Вы можете получить/установить/вызывать членов по путям или создать геттер/сеттер (lambda, скомпилированный в делегат), который быстрее, чем отражение. Например:

var success = Reflector.Get(DateTime.Now, null, "Date.Year", out int value);

Или создайте геттер один раз и кеш для повторного использования (это более результативно, но может вызывать исключение NullReferenceException, если промежуточный член равен нулю):

var getter = Reflector.CreateGetter<DateTime, int>("Date.Year", typeof(DateTime));
getter(DateTime.Now);

Или, если вы хотите создать List<Action<object, object>> разных геттеров, просто укажите базовые типы для скомпилированных делегатов (типы конверсий будут добавлены в скомпилированные lambdas):

var getter = Reflector.CreateGetter<object, object>("Date.Year", typeof(DateTime));
getter(DateTime.Now);
  • 0
    никогда не используйте сторонние библиотеки, если вы можете реализовать его в своем коде в разумные сроки в 5-10 строк.
0

Метод ниже подходит для меня идеально:

class MyClass {
    public string prop1 { set; get; }

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

Чтобы получить значение свойства:

MyClass t1 = new MyClass();
...
string value = t1["prop1].ToString();

Чтобы установить значение свойства:

t1["prop1] = value;
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;
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню