C# 使用 GetProperty 获取子属性的最佳方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/366332/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to get sub properties using GetProperty
提问by Todd Smith
public class Address
{
public string ZipCode {get; set;}
}
public class Customer
{
public Address Address {get; set;}
}
how can I access eitther "ZipCode" or "Address.ZipCode" with reflection? For example:
如何通过反射访问“ZipCode”或“Address.ZipCode”?例如:
Typeof(Customer).GetProperty("ZipCode")?
采纳答案by Jon Skeet
You'd need something like:
你需要这样的东西:
PropertyInfo addressProperty = typeof(Customer).GetProperty("Address");
ProportyInfo zipCodeProperty = addressProperty.PropertyType.GetProperty("ZipCode");
object address = addressProperty.GetValue(customer, null);
object zipCode = zipCodeProperty.GetValue(address, null);
Basically if you want to take a string "Address.ZipCode" and navigate down it, you need to split it by "." and then call GetProperty on the appropriate type at every step to get the property itself, then PropertyInfo.GetValue to get the next value in the chain. Something like this:
基本上,如果你想获取一个字符串“Address.ZipCode”并向下导航,你需要用“.”分割它。然后在每一步调用适当类型的 GetProperty 以获取属性本身,然后通过 PropertyInfo.GetValue 获取链中的下一个值。像这样的东西:
public static object FollowPropertyPath(object value, string path)
{
Type currentType = value.GetType();
foreach (string propertyName in path.Split('.'))
{
PropertyInfo property = currentType.GetProperty(propertyName);
value = property.GetValue(value, null);
currentType = property.PropertyType;
}
return value;
}
Call it like this:
像这样调用它:
object zipCode = FollowPropertyPath(customer, "Address.ZipCode");
Note that this works on the compile-time types of the properties. If you want it to cope with the execution time type (e.g. if customer.Address didn't have a ZipCode property, but the actual type returned by Address did) then change property.PropertyType
to property.GetType()
.
请注意,这适用于属性的编译时类型。如果您希望它处理执行时间类型(例如,如果 customer.Address 没有 ZipCode 属性,但 Address 返回的实际类型有),则更property.PropertyType
改为property.GetType()
.
Also note that this doesn't have any error handling etc :)
另请注意,这没有任何错误处理等:)
回答by maxnk
typeof (Customer).GetProperty("Address").PropertyType.GetProperty("ZipCode")
回答by Marc Gravell
The existing answers are fine; just an alternative perspective: in many scenarios it is desirable to use System.ComponentModel rather than direct reflection, as this allows for runtime property scenarios - i.e. how a DataTable's DataView exposes the columns as properties.
现有的答案很好;只是另一种观点:在许多情况下,最好使用 System.ComponentModel 而不是直接反射,因为这允许运行时属性方案 - 即 DataTable 的 DataView 如何将列公开为属性。
Performance wise - by default this is largely identical, but if you are doing lots of this (for example, bulk data import/export), you can actually get significant performance increases using this approach, courtesy of HyperDescriptor.
性能明智 - 默认情况下,这在很大程度上是相同的,但如果您正在执行大量此类操作(例如,批量数据导入/导出),则使用这种方法实际上可以获得显着的性能提升,由HyperDescriptor 提供。
To use System.ComponentModel, the code is similar, but subtly different:
使用 System.ComponentModel,代码类似,但略有不同:
static void Main()
{
object obj = new Customer { Address = new Address { ZipCode = "abcdef" } };
object address = GetValue(obj, "Address");
object zip = GetValue(address, "ZipCode");
Console.WriteLine(zip);
}
static object GetValue(object component, string propertyName)
{
return TypeDescriptor.GetProperties(component)[propertyName].GetValue(component);
}
This then gives you the same handling as though you had used data-binding to bind to "Address.ZipCode" (glossing over some details like lists etc).
然后,这为您提供了与使用数据绑定绑定到“Address.ZipCode”相同的处理方式(忽略了一些细节,如列表等)。
(note that you could cast zip as string etc if you know that is the expected type)
(请注意,如果您知道这是预期的类型,您可以将 zip 转换为字符串等)
To get the value from a deep path (including the same list handling that data-binding uses), you would use something like:
要从深层路径获取值(包括数据绑定使用的相同列表处理),您可以使用以下内容:
static object ResolveValue(object component, string path) {
foreach(string segment in path.Split('.')) {
if (component == null) return null;
if(component is IListSource) {
component = ((IListSource)component).GetList();
}
if (component is IList) {
component = ((IList)component)[0];
}
component = GetValue(component, segment);
}
return component;
}
The list stuff roughlymirrors the behaviour of regular data-binding (although it omits a few things like binding-contexts, currency-managers, etc)
列表内容大致反映了常规数据绑定的行为(尽管它省略了一些内容,例如绑定上下文、货币管理器等)
回答by Mike Fuchs
Jon Skeet's answer is fine, I had to extend his method a bit though, in order to account for derived instances within the property path:
Jon Skeet 的回答很好,但我不得不稍微扩展他的方法,以便考虑属性路径中的派生实例:
public static class ReflectorUtil
{
public static object FollowPropertyPath(object value, string path)
{
if (value == null) throw new ArgumentNullException("value");
if (path == null) throw new ArgumentNullException("path");
Type currentType = value.GetType();
object obj = value;
foreach (string propertyName in path.Split('.'))
{
if (currentType != null)
{
PropertyInfo property = null;
int brackStart = propertyName.IndexOf("[");
int brackEnd = propertyName.IndexOf("]");
property = currentType.GetProperty(brackStart > 0 ? propertyName.Substring(0, brackStart) : propertyName);
obj = property.GetValue(obj, null);
if (brackStart > 0)
{
string index = propertyName.Substring(brackStart + 1, brackEnd - brackStart - 1);
foreach (Type iType in obj.GetType().GetInterfaces())
{
if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
obj = typeof(ReflectorUtil).GetMethod("GetDictionaryElement")
.MakeGenericMethod(iType.GetGenericArguments())
.Invoke(null, new object[] { obj, index });
break;
}
if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof(IList<>))
{
obj = typeof(ReflectorUtil).GetMethod("GetListElement")
.MakeGenericMethod(iType.GetGenericArguments())
.Invoke(null, new object[] { obj, index });
break;
}
}
}
currentType = obj != null ? obj.GetType() : null; //property.PropertyType;
}
else return null;
}
return obj;
}
public static TValue GetDictionaryElement<TKey, TValue>(IDictionary<TKey, TValue> dict, object index)
{
TKey key = (TKey)Convert.ChangeType(index, typeof(TKey), null);
return dict[key];
}
public static T GetListElement<T>(IList<T> list, object index)
{
return list[Convert.ToInt32(index)];
}
}
Using property.PropertyType will get you the property type defined on the obj class, while using obj.GetType() will get you the actual type of the property's instance.
使用 property.PropertyType 将获得在 obj 类上定义的属性类型,而使用 obj.GetType() 将获得属性实例的实际类型。
EDIT: @Oliver - you're absolutely right, thanks for noting that. I adjusted the method to allow for generic Lists and Dictionaries. While I don't like the parsing part, I used Marc Gravell's clever idea in this threadto get the indexer property's values.
编辑:@Oliver - 你说得对,谢谢你注意到这一点。我调整了方法以允许通用列表和字典。虽然我不喜欢解析部分,但我在这个线程中使用了 Marc Gravell 的聪明想法来获取索引器属性的值。
回答by EmbraceUnity
adabyron,
阿达拜伦,
I created a version of your code for when you only need to grab the types, and if you don't have an actual object instance.
我为您只需要获取类型以及没有实际对象实例的情况创建了一个代码版本。
public static Type FollowPropertyPath<T>(string path)
{
if (path == null) throw new ArgumentNullException("path");
Type currentType = typeof(T);
foreach (string propertyName in path.Split('.'))
{
int brackStart = propertyName.IndexOf("[");
var property = currentType.GetProperty(brackStart > 0 ? propertyName.Substring(0, brackStart) : propertyName);
if (property == null)
return null;
currentType = property.PropertyType;
if (brackStart > 0)
{
foreach (Type iType in currentType.GetInterfaces())
{
if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof (IDictionary<,>))
{
currentType = iType.GetGenericArguments()[1];
break;
}
if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof (ICollection<>))
{
currentType = iType.GetGenericArguments()[0];
break;
}
}
}
}
return currentType;
}
回答by Eric McLachlan
Problem: Weakly Typed Variables:
问题:弱类型变量:
@jonskeet's FollowPropertyPath(...) method almostmet my needs exactly; except that
my property was weakly typed; therefore, currentType = property.PropertyType
simply returned System.Object and failed on the next iteration of the foreach-loop.
@jonskeet 的 FollowPropertyPath(...) 方法几乎完全满足了我的需求;除了我的属性是弱类型的;因此,currentType = property.PropertyType
只需返回 System.Object 并在 foreach 循环的下一次迭代中失败。
Solution:To use the runtime type rather than the design-time type, I adjusted the method as follows:
解决方案:为了使用运行时类型而不是设计时类型,我对方法进行了如下调整:
public static object FollowPropertyPath(object value, string path)
{
Type currentType = value.GetType();
foreach (string propertyName in path.Split('.'))
{
PropertyInfo property = currentType.GetProperty(propertyName);
value = property.GetValue(value, null);
currentType = value.GetType(); // <-- Change
}
return value;
}