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()私は特にそれが2回呼び出されるのが好きではありません。

どうすればもっとうまく書き直すことができますか?理想的には、シンプルなワンライナーにできれば最高です。

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したがって、最初のステップでプロパティをスキップするために使用されるよりも効率的です。

  • 演算子の後には、 1 つだけWhereを持つプロパティが残ります。この属性のないプロパティは除外されており、複数回適用することはできません。したがって、の代わりに使用できます。 DataMemberAttributeAttribute.GetCustomAttributeAttribute.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

プロパティ自体ではなく、プロパティの値を取得する必要があったため、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 に答える
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 に答える