使用C#中的reflection从string获取属性值

我正在尝试在我的代码中使用reflection 1示例实现数据转换 。

GetSourceValue函数有一个比较各种types的开关,但我想删除这些types和属性,并使用GetSourceValue只使用一个string作为参数获取属性的值。 我想传递string中的类和属性并parsing属性的值。

这可能吗?

1 原始博客post的Web Archive版本

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

当然,你会想添加validation和什么,但这是它的要点。

怎么样这样的事情:

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

这将允许您使用单个string下降到属性,如下所示:

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

您可以使用这些方法作为静态方法或扩展。

那么使用Microsoft.VisualBasic命名空间的CallByNameMicrosoft.VisualBasic.dll )怎么样? 它使用reflection来获取普通对象,COM对象,甚至dynamic对象的属性,字段和方法。

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

接着

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

添加到任何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"]; 

jheddings很好的回答。 我想改进它允许引用聚合数组或对象集合,以便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; } 

如果我使用Ed S.的代码,我会得到

'ReflectionExtensions.GetProperty(Type,string)'由于其保护级别而不可访问

看来, GetProperty()在Xamarin.Forms中不可用。 TargetFrameworkProfile是我的可移植类库(.NET Framework 4.5,Windows 8,ASP.NET Core 1.0,Xamarin.Android,Xamarin.iOS,Xamarin.iOS Classic)中的Profile7。

现在我find了一个工作解决scheme

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

资源

使用System.Reflection命名空间的PropertyInfo。 无论我们尝试访问什么属性,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<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; } 

这是一种在列表中获取所有属性值的方法。

这是另一种查找嵌套属性的方法,它不需要string来告诉你嵌套path。 感谢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); } 

关于嵌套属性的讨论,如果你使用DataBinder.Eval Method (Object, String) ,你可以避免所有的reflection东西:

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

当然,您需要添加对System.Web程序集的引用,但这可能不是什么大不了的事情。

你永远不会提到你正在检查的对象,既然你拒绝那些引用给定对象的对象,我会认为你的意思是静态的。

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

请注意,我使用局部variablesobj标记了正在检查的objnull表示静态,否则将其设置为你想要的。 另外请注意, GetEntryAssembly()是获得“正在运行”程序集的一些可用方法之一,如果您在加载该types时遇到困难,可能需要使用它。

以下代码是recursion方法,用于显示对象实例中包含的所有属性名称和值的整个层次结构。 此方法在此线程中使用上面的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'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); } } } 

在.NET标准中调用的方法已经改变(从1.6开始)。 我们也可以使用C#6的空条件运算符。

 using System.Reflection; public static object GetPropValue(object src, string propName) { return src.GetType().GetRuntimeProperty(propName)?.GetValue(src); } 
 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都写了如何解决属性string的优秀答案。 我想把它放在一起,因为我专门为此写了一个专门的库。

Pather.CSharp的主要类是Resolver 。 默认情况下,它可以parsing属性,数组和字典条目。

所以,例如,如果你有这样一个对象

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

并想获得Property2 ,你可以这样做:

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

这是它可以解决的path的最基本的例子。 如果你想看看还有什么可以的,或者你可以怎样扩展它,可以直接到它的Github页面 。

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