8

如何反思性地获取具有给定名称的 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()被调用两次。

如何更好地重写它?理想情况下,如果我能把它做成一个简单的单线就太好了。

4

4 回答 4

13
// 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.

于 2013-02-03T11:25:03.537 回答
2

您可以使用 LINQ:

string dataMemberName = ...;
var propInfo =
    (from property in typeof(T).GetProperties()
    let attributes = property
        .GetCustomAttributes(typeof(DataMemberAttribute), false)
        .OfType<DataMemberAttribute>()
    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<DataMemberAttribute>()
        .Any(x => x.Name == dataMemberName)
    )
    .FirstOrDefault();
于 2013-02-03T10:32:51.497 回答
1

你可以使用Fasterflect让你的反射代码更简单、更容易看:

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<DataMemberAttribute>( Flags.InstancePublic )
    .Where( p => p.Name == dataMemberName ).FirstOrDefault();

Fasterflect 带有一组不错的扩展方法,如果您还需要速度,还可以使用 IL 生成进行一些简洁的性能优化。

于 2013-02-03T11:10:48.137 回答
1

我需要获取属性的值,而不是属性本身,因此使用了Darin Dimitrov 的答案,但添加.GetValue(this)到末尾以返回值。

这是我的课最终的样子:

[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<DataMemberAttribute>()
                              .Any(x => x.Name == name))).GetValue(this);
    }
}
于 2016-02-25T10:18:14.633 回答