使用C#中的反射从字符串获取属性值

您所在的位置:网站首页 selfvalue 使用C#中的反射从字符串获取属性值

使用C#中的反射从字符串获取属性值

2023-04-05 20:44| 来源: 网络整理| 查看: 265

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

GetSourceValue函数有一个比较各种类型的开关,但是我想删除这些类型和属性,并让GetSourceValue只使用一个字符串作为参数来获取属性的值。我想在字符串中传递一个类和属性,并解析该属性的值。

这有可能吗?

1Web Archive version of Original Blog Post

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

当然,您需要添加验证和其他内容,但这就是其中的要点。

相关讨论 是的,我知道我可以像你说的那样使用。但我不想传递SRC对象。我只想传递一个名为"class1.prop1"的字符串,并给我class1类的prop1值。 你在问题"我要通过班级"中问的,我想这是"对象"而不是"班级",因为"班级"没有意义。你为什么要投反对票?你不知道如何修改这个代码来使用"this"吗?它是类的静态属性吗?你需要更具体一些,这是正确的答案。 src.gettype().getproperty(propname).getValue(src,null)为嵌套对象返回空值。我的意思是像school.employee.name在本例中是school.getType().getProperty(employee.name).getValue(school,空) @穆拉里:因为那是错误的。您需要先获取Employee属性的值,然后再次执行相同的操作。 @我正在处理一个需要将一些值(属性)移动到同一类型的其他对象的需求。我还得到字符串列表,其中包含我需要复制该值的属性名。例如"name"、"employee.name"等。我需要做一个反射以从obja获取这些属性值,并将其设置为objb。我有办法处理吗?到目前为止,我编写了一个通用方法来获取给定属性名的属性值。代码正好低于jheddings answer的代码。我所需要的只是如何用obja的新值设置另一个对象(同一类型,objb)?有什么想法或帮助吗? @艾德,不起作用。我错过了什么? @汤姆和第225;?扎托:它不起作用,因为test不是一个属性,而是一个字段。使用属性或使用适当的反射调用获取字段值。只是好奇,你对这个答案投了反对票吗?如果是这样,也许你应该重新考虑。 @是的,对不起。我完全不知道在C中有一个叫做field的东西,而不是财产。我的投票被锁定了。我能做什么? @汤姆和第225;?扎托:没关系,投反对票也没什么大不了的,不过在将来投反对票之前,要确定你知道你在说什么。 又好又简单!不过,我会把它变成通用的:public static T GetPropertyValue(object obj, string propName) { return (T)obj.GetType().GetProperty(propName).GetValue(obj, null); }。 您应该包括一个示例,不清楚SRC应该包含什么。 @罗尔斯:嗯?src是一个对象…GetType是在对象上定义的…你想做什么就做什么。 对不起,我知道怎么用。我只是觉得如果你用一个如何使用函数的例子来展示更多的行,可能会更整洁一些,因为它可能不会立即清晰。 @滚动,你可以在任何类型的对象上执行这个操作。 优化可以消除这样的空异常风险:"src.GetType().GetProperty(propName)?.GetValue(src, null);"。 @夏:我觉得这是个坏主意。如何区分现有属性的空值或根本没有属性?我宁愿立刻知道我寄来了一个不好的地名。这不是生产代码,但更好的改进是抛出更具体的异常(例如,检查GetProperty上的空值,如果为空则抛出PropertyNotFoundException或其他东西)。

像这样的怎么样:

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

这将允许您使用单个字符串下降到属性中,如下所示:

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

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

相关讨论 @Fredjand很高兴你偶然发现了它!当这些旧的帖子出现时总是令人惊讶。它有点模糊,所以我加了一点文字来解释它。我还使用这些作为扩展方法,并添加了一个泛型表单,所以我在这里添加了它。 为什么前臂中的空保护不在上面? @Santhos因为在foreach循环体中重新定义了"obj",所以在每次迭代中都会检查它。

添加到任何Class中:

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

然后,您可以将其用作:

12345Foo f = new Foo(); // Set f["Bar"] ="asdf"; // Get string s = (string)f["Bar"]; 相关讨论 @爱德华多科莫:有没有可能用反射来处理这个问题,这样你就不需要知道这个班有哪些成员? 如果"bar"是一个对象,是否可以这样做? 使用字符串键使对象像字典一样!!!!令人惊叹的 @大水:SetValue和GetValue方法与Object一起工作。如果需要使用特定类型,则应强制转换GetValue的结果,并强制转换值以将其与SetValue赋值。 抱歉,我不能理解你的问题。你想做什么? 此类型方法的名称是什么….?

使用Microsoft.VisualBasic名称空间(Microsoft.VisualBasic.dll)的CallByName怎么样?它使用反射来获取普通对象、COM对象甚至动态对象的属性、字段和方法。

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

然后

1Versioned.CallByName(this,"method/function/prop name", CallType.Get).ToString(); 相关讨论 有意思的建议,进一步的检查证明,它既可以处理字段和属性,也可以处理COM对象,甚至可以正确处理动态绑定! 这应该是所有人的正确答案:)

杰丁斯的回答很好。我希望改进它,允许引用聚合数组或对象集合,以便propertyname可以是property1.property2[x].property3:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758    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][1]访问的列表列表怎么样?

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

'ReflectionExtensions.GetProperty(Type, string)' is inaccessible due to its protection level

似乎没有Xamarin.forms提供GetProperty()。TargetFrameworkProfile是我的可移植类库(.NET Framework 4.5、Windows 8、ASP.NET Core 1.0、Xamarin.android、Xamarin.ios、Xamarin.ios classic)中的Profile7。

现在我找到了一个可行的解决方案:

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

来源

相关讨论 只是一个小小的改进。将if和next返回替换为:返回属性?.getvalue(源);

关于嵌套属性的讨论,如果使用DataBinder.Eval Method (Object, String)如下所示,则可以避免所有反射内容:

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

当然,您需要添加对System.Web组件的引用,但这可能不是什么大问题。

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

12345using System.Reflection; public static object GetPropValue(object src, string propName) {     return src.GetType().GetRuntimeProperty(propName)?.GetValue(src); } 相关讨论 用于使用? operator。

使用System.Reflection命名空间的PropertyInfo。无论我们试图访问什么属性,反射编译都很好。运行时出错。

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

它可以很好地获取对象的位置属性

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

我们会得到位置:x=71,y=27我们也可以用同样的方法返回location.x或location.y。

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

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

相关讨论 为什么要这样做:当它对变量pi==时,type.GetProperty(pi.Name)? 如果你使用c 6.0,去掉if,做selfValue?.ToString(),否则去掉if,使用selfValue==null?null:selfValue.ToString()。 还有一个List的列表是奇数,使用字典Dictionary。

123456789public 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"];

你从来没有提到你要检查的对象,而且由于你拒绝了引用一个给定对象的对象,我假设你是指一个静态对象。

12345678using 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()是获得"正在运行"程序集的几种可用方法之一,如果在加载类型时遇到困难,您可能希望使用它。

下面的代码是一个递归方法,用于显示对象实例中包含的所有属性名和值的整个层次结构。这个方法在这个线程中使用了上面Alexd的GetPropertyValue()答案的简化版本。多亏了这条讨论线,我才知道该怎么做!

例如,我使用此方法通过调用以下方法来显示WebService响应中所有属性的爆炸或转储:

PropertyValues_byRecursion("Response", response, false);

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990public 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);     }   } } 12345678910111213public 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); }

这是另一种查找嵌套属性的方法,不需要字符串来告诉您嵌套路径。对于单属性方法,请归功于Ed S.。

123456789101112131415161718192021222324252627    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);     } 相关讨论 最好检查Type.GetProperty是否返回null,而不是调用GetValue,并在循环中抛出NullReferenceException。

更短的路……

12345var 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的主要阶级是Resolver。默认情况下,它可以解析属性、数组和字典条目。

例如,如果有这样的对象

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

想要得到Property2,你可以这样做:

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

这是最基本的路径示例。如果你想知道它还能做些什么,或者你如何扩展它,就直接进入它的Github页面。

1Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable,"YourVariableName", CallType), YourType)

以下方法非常适合我:

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

要获取属性值:

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

要设置属性值:

1t1["prop1"] = value;

看看Heleonix.Reflection库。您可以通过路径获取/设置/调用成员,或者创建一个比反射更快的getter/setter(编译为委托的lambda)。例如:

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

或者创建一次getter并缓存以供重用(这更具性能,但如果中间成员为空,则可能引发NullReferenceException):

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

或者,如果要创建不同getter的List>,只需为已编译委托指定基类型(类型转换将添加到已编译的lambda中):

12var getter = Reflector.CreateGetter("Date.Year", typeof(DateTime)); getter(DateTime.Now); 相关讨论 如果您可以在合理的时间内用5-10行代码在自己的代码中实现,就不要使用第三方libs。

这是我根据其他答案得到的结果。对错误处理的具体化有点过分。

123456789101112131415161718192021222324252627282930313233343536373839404142434445public static T GetPropertyValue(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对象访问集合/数组项。

1234567891011121314151617181920212223242526272829303132333435363738394041424344public 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


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3