使用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
命名空间的CallByName
( Microsoft.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
标记了正在检查的obj
。 null
表示静态,否则将其设置为你想要的。 另外请注意, 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); }