Get property value from string using reflection in C#
我正在尝试在代码中使用Reflection1示例实现数据转换。
这有可能吗?
1Web Archive version of Original Blog Post
1 2 3 4 | public static object GetPropValue(object src, string propName) { return src.GetType().GetProperty(propName).GetValue(src, null); } |
当然,您需要添加验证和其他内容,但这就是其中的要点。
像这样的怎么样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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; } |
这将允许您使用单个字符串下降到属性中,如下所示:
1 2 3 | DateTime now = DateTime.Now; int min = GetPropValue<int>(now,"TimeOfDay.Minutes"); int hrs = now.GetPropValue<int>("TimeOfDay.Hours"); |
您可以将这些方法用作静态方法或扩展。
添加到任何
1 2 3 4 5 6 7 8 9 10 | 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; } } |
然后,您可以将其用作:
1 2 3 4 5 |
使用
1 2 | using Microsoft.VisualBasic; using Microsoft.VisualBasic.CompilerServices; |
然后
1 | Versioned.CallByName(this,"method/function/prop name", CallType.Get).ToString(); |
杰丁斯的回答很好。我希望改进它,允许引用聚合数组或对象集合,以便propertyname可以是property1.property2[x].property3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | 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)' is inaccessible due to its protection level
似乎没有Xamarin.forms提供
现在我找到了一个可行的解决方案:
1 2 3 4 5 6 7 8 | 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); } |
来源
关于嵌套属性的讨论,如果使用
1 | var value = DataBinder.Eval(DateTime.Now,"TimeOfDay.Hours"); |
当然,您需要添加对
.NET标准中要调用的方法已更改(从1.6开始)。我们还可以使用C 6的空条件运算符。
1 2 3 4 5 | using System.Reflection; public static object GetPropValue(object src, string propName) { return src.GetType().GetRuntimeProperty(propName)?.GetValue(src); } |
使用System.Reflection命名空间的PropertyInfo。无论我们试图访问什么属性,反射编译都很好。运行时出错。
1 2 3 4 5 6 7 | 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; } |
它可以很好地获取对象的位置属性
1 | Label1.Text = GetObjProperty(button1,"Location").ToString(); |
我们会得到位置:x=71,y=27我们也可以用同样的方法返回location.x或location.y。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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; } |
这是一种在列表中获取所有属性及其值的方法。
1 2 3 4 5 6 7 8 9 | 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"]; |
你从来没有提到你要检查的对象,而且由于你拒绝了引用一个给定对象的对象,我假设你是指一个静态对象。
1 2 3 4 5 6 7 8 | 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); } |
注意,我用局部变量
下面的代码是一个递归方法,用于显示对象实例中包含的所有属性名和值的整个层次结构。这个方法在这个线程中使用了上面Alexd的
例如,我使用此方法通过调用以下方法来显示
PropertyValues_byRecursion("Response", response, false);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | 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); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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); } |
这是另一种查找嵌套属性的方法,不需要字符串来告诉您嵌套路径。对于单属性方法,请归功于Ed S.。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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); } |
更短的路……
1 2 3 4 5 | 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都写了关于如何解析属性字符串的很好的答案。我想把我的混合,因为我写了一个专门的图书馆正是为了这个目的。
探路者。CSharp的主要阶级是
例如,如果有这样的对象
想要得到
1 2 3 4 | IResolver resolver = new Resolver(); var path ="Property1.Property2"; object result = r.Resolve(o, path); //=>"value" |
这是最基本的路径示例。如果你想知道它还能做些什么,或者你如何扩展它,就直接进入它的Github页面。
1 | Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable,"YourVariableName", CallType), YourType) |
以下方法非常适合我:
1 2 3 4 5 6 7 8 9 | 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); } } } |
要获取属性值:
1 2 3 |
要设置属性值:
1 | t1["prop1"] = value; |
看看Heleonix.Reflection库。您可以通过路径获取/设置/调用成员,或者创建一个比反射更快的getter/setter(编译为委托的lambda)。例如:
1 | var success = Reflector.Get(DateTime.Now, null,"Date.Year", out int value); |
或者创建一次getter并缓存以供重用(这更具性能,但如果中间成员为空,则可能引发NullReferenceException):
1 2 | var getter = Reflector.CreateGetter<DateTime, int>("Date.Year", typeof(DateTime)); getter(DateTime.Now); |
或者,如果要创建不同getter的
1 2 | var getter = Reflector.CreateGetter<object, object>("Date.Year", typeof(DateTime)); getter(DateTime.Now); |
这是我根据其他答案得到的结果。对错误处理的具体化有点过分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | public static T GetPropertyValue<T>(object sourceInstance, string targetPropertyName, bool throwExceptionIfNotExists = false) { string errorMsg = null; try { if (sourceInstance == null || string.IsNullOrWhiteSpace(targetPropertyName)) { errorMsg = $"Source object is null or property name is null or whitespace. '{targetPropertyName}'"; Log.Warn(errorMsg); if (throwExceptionIfNotExists) throw new ArgumentException(errorMsg); else return default(T); } Type returnType = typeof(T); Type sourceType = sourceInstance.GetType(); PropertyInfo propertyInfo = sourceType.GetProperty(targetPropertyName, returnType); if (propertyInfo == null) { errorMsg = $"Property name '{targetPropertyName}' of type '{returnType}' not found for source object of type '{sourceType}'"; Log.Warn(errorMsg); if (throwExceptionIfNotExists) throw new ArgumentException(errorMsg); else return default(T); } return (T)propertyInfo.GetValue(sourceInstance, null); } catch(Exception ex) { errorMsg = $"Problem getting property name '{targetPropertyName}' from source instance."; Log.Error(errorMsg, ex); if (throwExceptionIfNotExists) throw; } return default(T); } |
这是我的解决方案。它还与COM对象一起工作,并允许从COM对象访问集合/数组项。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | 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; } |