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

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

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

Это возможно?

1 версия веб-архива оригинальной записи в блоге

    21 Solutions collect form web for “Получить значение свойства из строки, используя reflection в C #”

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

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

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

     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(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(now, "TimeOfDay.Minutes"); int hrs = now.GetPropValue("TimeOfDay.Hours"); 

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

    Добавить в любой 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"]; 

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

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

    а потом

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

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

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

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

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

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

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

    Кажется, что GetProperty() недоступен в Xamarin.Forms. TargetFrameworkProfile – это Profile7 в моей переносимой библиотеке classов (.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; } 

    Источник

    Использование 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 таким же образом.

     public static List> GetProperties(object item) //where T : class { var result = new List>(); 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(pi.Name, selfValue.ToString())); } else { result.Add(new KeyValuePair(pi.Name, null)); } } } return result; } 

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

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

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

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

      public static T FindNestedPropertyValue(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(currentProperty, propName); found = true; } catch { } } } if (!found) { throw new Exception("Unable to find property: " + propName); } return retVal; } public static T GetPropValue(object srcObject, string propName) { return (T)srcObject.GetType().GetProperty(propName).GetValue(srcObject, null); } 

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

     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() является одним из нескольких доступных способов получения «бегущей» сборки, вы можете захотеть поиграть с ней, если вам тяжело загружать этот тип.

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

    Например, я использую этот метод, чтобы показать взрыв или дамп всех свойств в ответе 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); } } } 
     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"]; 
     Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable, "YourVariableName", CallType), YourType) 

    более короткий путь ….

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

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

    Основной class Pather.CSharpResolver . По умолчанию он может разрешать записи свойств, массива и словаря.

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

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

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

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

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

     public static TValue GetFieldValue(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(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); } 

    Вот мое решение. Он также работает с объектами 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; } 

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

     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; 

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

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

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

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

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

     var getter = Reflector.CreateGetter("Date.Year", typeof(DateTime)); getter(DateTime.Now); 
    Давайте будем гением компьютера.