给出以下对象:
public class Customer {
public String Name { get; set; }
public String Address { get; set; }
}
public class Invoice {
public String ID { get; set; }
public DateTime Date { get; set; }
public Customer BillTo { get; set; }
}
我想使用反射来浏览Invoice
以获取Name
的{{1}}属性。这就是我所追求的,假设这段代码可行:
Customer
当然,由于“BillTo.Address”不是Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
类的有效属性,因此失败。
所以,我尝试编写一个方法将字符串拆分成句点,然后遍历对象,寻找我感兴趣的最终值。它可以正常工作,但我对它并不是很满意:
Invoice
有关如何改进此方法的任何想法,或解决此问题的更好方法?
编辑发布后,我看到了一些相关的帖子......但似乎并没有专门针对这个问题的答案。另外,我仍然喜欢我的实施反馈。
答案 0 :(得分:11)
我实际上认为你的逻辑很好。就个人而言,我可能会更改它,因此您将对象作为第一个参数传递(与PropertyInfo.GetValue更内联,因此不那么令人惊讶)。
我也可能会把它称为更像GetNestedPropertyValue的东西,以明确它搜索属性堆栈。
答案 1 :(得分:10)
您必须访问需要使用反射的ACTUAL对象。这就是我的意思:
而不是:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
这样做(根据评论编辑):
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo");
Customer cust = (Customer)info.GetValue(inv, null);
PropertyInfo info2 = cust.GetType().GetProperty("Address");
Object val = info2.GetValue(cust, null);
查看这篇文章了解更多信息: Using reflection to set a property of a property of an object
答案 2 :(得分:9)
我知道我在聚会上有点迟了,正如其他人所说,你的实施很好
... 用于简单用例。
但是,我已经开发了一个库,可以完全解决该用例Pather.CSharp
它也可以Nuget Package获得。
它的主要类是Resolver
,其Resolve
方法
您传递对象和 属性路径,它将返回所需值。
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Address");
但它也可以解析更多复杂属性路径,包括数组和字典访问
因此,例如,如果您的Customer
有多个地址
public class Customer {
public String Name { get; set; }
public IEnumerable<String> Addresses { get; set; }
}
您可以使用Addresses[1]
访问第二个。
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Addresses[1]");
答案 3 :(得分:8)
我使用此方法从
等属性中获取值<强>&#34;属性&#34; 强>
<强>&#34; Address.Street&#34; 强>
<强>&#34; Address.Country.Name&#34; 强>
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
if(propName.Contains("."))//complex type nested
{
var temp = propName.Split(new char[] { '.' }, 2);
return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
}
else
{
var prop = src.GetType().GetProperty(propName);
return prop != null ? prop.GetValue(src, null) : null;
}
}
答案 4 :(得分:7)
希望对派对听起来不太晚,我想补充一下我的解决方案: 在这种情况下肯定使用递归
public static Object GetPropValue(String name, object obj, Type type)
{
var parts = name.Split('.').ToList();
var currentPart = parts[0];
PropertyInfo info = type.GetProperty(currentPart);
if (info == null) { return null; }
if (name.IndexOf(".") > -1)
{
parts.Remove(currentPart);
return GetPropValue(String.Join(".", parts), info.GetValue(obj, null), info.PropertyType);
} else
{
return info.GetValue(obj, null).ToString();
}
}
答案 5 :(得分:6)
你没有解释你的“不适”的来源,但你的代码对我来说基本上是合理的。
我唯一要问的是错误处理。如果代码尝试遍历空引用或者属性名称不存在,则返回null。这隐藏了错误:很难知道它是否因为没有BillTo客户而返回null,或者因为你拼错了它“BilTo.Address”......或者因为有一个BillTo客户,并且它的地址为空!在这些情况下,我会让方法崩溃并烧掉 - 只是让异常逃脱(或者将它包装在更友好的中)。
答案 6 :(得分:2)
> Get Nest properties e.g., Developer.Project.Name
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName)
{
if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
if (PropertName.Split('.').Length == 1)
return t.GetType().GetProperty(PropertName);
else
return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]);
}
答案 7 :(得分:1)
if (info == null) { /* throw exception instead*/ }
如果他们请求不存在的属性,我实际上会抛出异常。你编码的方式,如果我调用GetPropValue并返回null,我不知道这是否意味着该属性不存在,或者属性确实存在,但它的值为null。
答案 8 :(得分:1)
public static string GetObjectPropertyValue(object obj, string propertyName)
{
bool propertyHasDot = propertyName.IndexOf(".") > -1;
string firstPartBeforeDot;
string nextParts = "";
if (!propertyHasDot)
firstPartBeforeDot = propertyName.ToLower();
else
{
firstPartBeforeDot = propertyName.Substring(0, propertyName.IndexOf(".")).ToLower();
nextParts = propertyName.Substring(propertyName.IndexOf(".") + 1);
}
foreach (var property in obj.GetType().GetProperties())
if (property.Name.ToLower() == firstPartBeforeDot)
if (!propertyHasDot)
if (property.GetValue(obj, null) != null)
return property.GetValue(obj, null).ToString();
else
return DefaultValue(property.GetValue(obj, null), propertyName).ToString();
else
return GetObjectPropertyValue(property.GetValue(obj, null), nextParts);
throw new Exception("Property '" + propertyName.ToString() + "' not found in object '" + obj.ToString() + "'");
}
答案 9 :(得分:1)
如果它是一个枚举器并且继续深入,那么跳过嵌套属性的另一个实现。类型字符串的属性不受枚举检查的影响。
public static class ReflectionMethods
{
public static bool IsNonStringEnumerable(this PropertyInfo pi)
{
return pi != null && pi.PropertyType.IsNonStringEnumerable();
}
public static bool IsNonStringEnumerable(this object instance)
{
return instance != null && instance.GetType().IsNonStringEnumerable();
}
public static bool IsNonStringEnumerable(this Type type)
{
if (type == null || type == typeof(string))
return false;
return typeof(IEnumerable).IsAssignableFrom(type);
}
public static Object GetPropValue(String name, Object obj)
{
foreach (String part in name.Split('.'))
{
if (obj == null) { return null; }
if (obj.IsNonStringEnumerable())
{
var toEnumerable = (IEnumerable)obj;
var iterator = toEnumerable.GetEnumerator();
if (!iterator.MoveNext())
{
return null;
}
obj = iterator.Current;
}
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
}
基于这个问题和
我在MVC项目中使用它来通过简单地传递Property来对其进行排序来动态地对我的数据进行排序 示例:强>
result = result.OrderBy((s) =>
{
return ReflectionMethods.GetPropValue("BookingItems.EventId", s);
}).ToList();
其中BookingItems是一个对象列表。
答案 10 :(得分:0)
当我需要解决同样的问题时,我的网络连接已关闭,所以我不得不'重新发明轮子':
static object GetPropertyValue(Object fromObject, string propertyName)
{
Type objectType = fromObject.GetType();
PropertyInfo propInfo = objectType.GetProperty(propertyName);
if (propInfo == null && propertyName.Contains('.'))
{
string firstProp = propertyName.Substring(0, propertyName.IndexOf('.'));
propInfo = objectType.GetProperty(firstProp);
if (propInfo == null)//property name is invalid
{
throw new ArgumentException(String.Format("Property {0} is not a valid property of {1}.", firstProp, fromObject.GetType().ToString()));
}
return GetPropertyValue(propInfo.GetValue(fromObject, null), propertyName.Substring(propertyName.IndexOf('.') + 1));
}
else
{
return propInfo.GetValue(fromObject, null);
}
}
非常确定这解决了您用于属性名称的任何字符串的问题,无论嵌套的程度如何,只要一切都是属性。
答案 11 :(得分:0)
我想分享自己的解决方案,尽管可能为时已晚。该解决方案主要是检查嵌套属性是否存在。但是如果需要,可以很容易地对其进行调整以返回属性值。
private static PropertyInfo _GetPropertyInfo(Type type, string propertyName)
{
//***
//*** Check if the property name is a complex nested type
//***
if (propertyName.Contains("."))
{
//***
//*** Get the first property name of the complex type
//***
var tempPropertyName = propertyName.Split(".", 2);
//***
//*** Check if the property exists in the type
//***
var prop = _GetPropertyInfo(type, tempPropertyName[0]);
if (prop != null)
{
//***
//*** Drill down to check if the nested property exists in the complex type
//***
return _GetPropertyInfo(prop.PropertyType, tempPropertyName[1]);
}
else
{
return null;
}
}
else
{
return type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
}
}
我不得不参考一些帖子来提出这个解决方案。我认为这将适用于多种嵌套属性类型。
答案 12 :(得分:0)
基于@jheddings 的原始代码,我创建了一个带有泛型类型和验证的扩展方法版本:
public static T GetPropertyValue<T>(this object sourceObject, string propertyName)
{
if (sourceObject == null) throw new ArgumentNullException(nameof(sourceObject));
if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentException(nameof(propertyName));
foreach (string currentPropertyName in propertyName.Split('.'))
{
if (string.IsNullOrWhiteSpace(currentPropertyName)) throw new InvalidOperationException($"Invalid property '{propertyName}'");
PropertyInfo propertyInfo = sourceObject.GetType().GetProperty(currentPropertyName);
if (propertyInfo == null) throw new InvalidOperationException($"Property '{currentPropertyName}' not found");
sourceObject = propertyInfo.GetValue(sourceObject);
}
return sourceObject is T result ? result : default;
}
答案 13 :(得分:0)
我写了一个方法,它从输入接收一个对象类型作为参数并返回字典
public static Dictionary<string, string> GetProperties(Type placeHolderType)
{
var result = new Dictionary<string, string>();
var properties = placeHolderType.GetProperties();
foreach (var propertyInfo in properties)
{
string name = propertyInfo.Name;
string description = GetDescriptionTitle(propertyInfo);
if (IsNonString(propertyInfo.PropertyType))
{
var list = GetProperties(propertyInfo.PropertyType);
foreach (var item in list)
{
result.Add($"{propertyInfo.PropertyType.Name}_{item.Key}", item.Value);
}
}
else
{
result.Add(name, description);
}
}
return result;
}
public static bool IsNonString(Type type)
{
if (type == null || type == typeof(string))
return false;
return typeof(IPlaceHolder).IsAssignableFrom(type);
}
private static string GetDescriptionTitle(MemberInfo memberInfo)
{
if (Attribute.GetCustomAttribute(memberInfo, typeof(DescriptionAttribute)) is DescriptionAttribute descriptionAttribute)
{
return descriptionAttribute.Description;
}
return memberInfo.Name;
}
答案 14 :(得分:-7)
尝试inv.GetType().GetProperty("BillTo+Address");