如何获取具有指定名称的DataMemberAttribute的属性?
如何reflection性地获取具有给定名称的DataMember的属性(让我们假设每个DataMember都有一个唯一的名称)? 例如,在以下代码中,具有名称“p1”的DataMember的属性是PropertyOne
:
[DataContract(Name = "MyContract")] public class MyContract { [DataMember(Name = "p1")] public string PropertyOne { get; set; } [DataMember(Name = "p2")] public string PropertyTwo { get; set; } [DataMember(Name = "p3")] public string PropertyThree { get; set; } }
目前,我有:
string dataMemberName = ...; var dataMemberProperties = typeof(T).GetProperties().Where(p => p.GetCustomAttributes(typeof(DataMemberAttribute), false).Any()); var propInfo = dataMemberProperties.Where(p => ((DataMemberAttribute)p.GetCustomAttributes(typeof(DataMemberAttribute), false).First()).Name == dataMemberName).FirstOrDefault();
这有效,但感觉可以改进。 我特别不喜欢GetCustomAttributes()
被调用两次。
如何更好地重写? 理想情况下,如果我能把它变成一个简单的单行程,那将是很棒的。
// using System.Linq; // using System.Reflection; // using System.Runtime.Serialization; obj.GetType() .GetProperties(…) .Where(p => Attribute.IsDefined(p, typeof(DataMemberAttribute))) .Single(p => ((DataMemberAttribute)Attribute.GetCustomAttribute( p, typeof(DataMemberAttribute))).Name == "Foo");
笔记:
-
Attribute.IsDefined
用于检查是否存在自定义属性而不检索其数据。 因此,它比Attribute.GetCustomAttribute
更有效,并用于在第一步中跳过属性。 -
在
Where
运算符之后,我们只剩下具有一个DataMemberAttribute
属性:没有此属性的属性已被过滤掉,并且不能多次应用。 因此,我们可以使用Attribute.GetCustomAttribute
而不是Attribute.GetCustomAttributes
。
您可以使用LINQ:
string dataMemberName = ...; var propInfo = (from property in typeof(T).GetProperties() let attributes = property .GetCustomAttributes(typeof(DataMemberAttribute), false) .OfType() where attributes.Any(a => a.Name == dataMemberName) select property).FirstOrDefault();
或者如果您愿意:
string dataMemberName = ...; var propInfo = typeof(T) .GetProperties() .Where(p => p .GetCustomAttributes(typeof(DataMemberAttribute), false) .OfType() .Any(x => x.Name == dataMemberName) ) .FirstOrDefault();
您可以使用Fasterflect使您的reflection代码更简单,更容易:
var property = typeof(T).MembersAndAttributes( MemberTypes.Property, typeof(DataMemberAttribute) ) .Where( ma => ma.Attributes.First().Name == dataMemberName ) .Select( ma => ma.Member as PropertyInfo ) .FirstOrDefault();
如果您只需要检查是否存在属性,则可以使用以下内容:
var property = typeof(T).PropertiesWith( Flags.InstancePublic ) .Where( p => p.Name == dataMemberName ).FirstOrDefault();
Fasterflect附带了一组很好的扩展方法,如果你还需要速度,还可以使用IL生成一些整洁的性能优化。
我需要获取属性的值而不是属性本身所以使用Darin Dimitrov的答案但是添加.GetValue(this)
到最后返回值。
这是我的class级最终看起来像:
[DataContract] public class Item { [DataMember(Name = "kpiId")] public string KPIId { get; set; } [DataMember(Name = "value")] public string Value { get; set; } [DataMember(Name = "unit")] public string Unit{ get; set; } [DataMember(Name = "status")] public string Status { get; set; } [DataMember(Name = "category")] public string Category { get; set; } [DataMember(Name = "description")] public string Description { get; set; } [DataMember(Name = "source")] public string Source { get; set; } [DataMember(Name = "messages")] public SysMessage[] Messages { get; set; } public object getDataMemberByName(string name) { return (typeof(Item).GetProperties().FirstOrDefault(p => p.GetCustomAttributes(typeof(DataMemberAttribute), false) .OfType() .Any(x => x.Name == name))).GetValue(this); } }