Yansıma - özellik adı ve değerini alın


253

Ben bir sınıf var, Adı adlı bir özellik ile Kitap diyelim. Bu özellik ile, onunla ilişkili bir özniteliğim var.

public class Book
{
    [Author("AuthorName")]
    public string Name
    {
        get; private set; 
    }
}

Benim ana yöntem, yansıma kullanıyorum ve her özellik için her özniteliğin anahtar değer çiftini almak istiyorum. Bu örnekte, öznitelik adı için "Author" ve öznitelik değeri için "AuthorName" ifadesini görmeyi beklerdim.

Soru: Yansıma kullanarak özelliklerimdeki özellik adını ve değerini nasıl alabilirim?


yansıma yoluyla mülkün o nesneye erişmeye çalıştığınızda ne oluyor, bir yere mi takıldınız yoksa yansıma için kod mu istiyorsunuz
kobe

Yanıtlar:


308

typeof(Book).GetProperties()Bir dizi PropertyInfoörneği almak için kullanın . Ardından , bunlardan herhangi birinin Öznitelik türüne sahip olup olmadığını görmek GetCustomAttributes()için her PropertyInfobirini kullanın Author. Varsa, özellik adını özellik bilgisinden ve özellik değerlerini özellikten alabilirsiniz.

Belirli bir öznitelik türüne sahip özellikler için bir tür taramak ve bir sözlükte veri döndürmek için bu satırlar boyunca bir şey (bu, türleri rutine geçirerek daha dinamik hale getirilebileceğini unutmayın):

public static Dictionary<string, string> GetAuthors()
{
    Dictionary<string, string> _dict = new Dictionary<string, string>();

    PropertyInfo[] props = typeof(Book).GetProperties();
    foreach (PropertyInfo prop in props)
    {
        object[] attrs = prop.GetCustomAttributes(true);
        foreach (object attr in attrs)
        {
            AuthorAttribute authAttr = attr as AuthorAttribute;
            if (authAttr != null)
            {
                string propName = prop.Name;
                string auth = authAttr.Name;

                _dict.Add(propName, auth);
            }
        }
    }

    return _dict;
}

16
Bu özelliği kullanmak zorunda kalmamamı umuyordum.
developerdoug

prop.GetCustomAttributes (true) yalnızca bir nesne [] döndürür. Yayınlamak istemiyorsanız, özellik örneklerinin kendileri üzerinde yansıma kullanabilirsiniz.
Adam Markowitz

Burada AuthorAttribute nedir? Öznitelikten türetilen bir sınıf mı? Adam Markowitz
Sarath Avanavu

1
Evet. OP, 'Author' adlı özel bir özellik kullanıyor. Bir örnek için buraya bakın: msdn.microsoft.com/en-us/library/sw480ze8.aspx
Adam Markowitz

1
Özelliğin yayınlanmasının performans maliyeti, ilgili tüm diğer işlemlere kıyasla (önemsiz denetim ve dize atamaları hariç) tamamen önemsizdir.
SilentSin

112

Sözlükteki bir özelliğin tüm niteliklerini almak için şunu kullanın:

typeof(Book)
  .GetProperty("Name")
  .GetCustomAttributes(false) 
  .ToDictionary(a => a.GetType().Name, a => a);

dan değiştirmeyi unutmayın falseiçin truesize sıra inheritted özelliklerini dahil etmek istiyorsanız.


3
Bu, Adem'in çözümü ile aynı şeyi etkili bir şekilde yapar, ancak çok daha özlüdür.
Daniel Moore

31
Yerine ToDictionary ifadesine Ekleme .OfType <AuthorAttribue> () yalnızca Yazar özelliklerini ihtiyaç ve gelecekteki döküm atlamak istiyorsanız
Adrian Zanescu

2
Aynı mülkte aynı türden iki öznitelik olduğunda bu istisna oluşturmaz mı?
Konstantin

54

Yalnızca belirli bir Öznitelik değeri istiyorsanız, örneğin Görüntü Özniteliği'ni aşağıdaki kodu kullanabilirsiniz:

var pInfo = typeof(Book).GetProperty("Name")
                             .GetCustomAttribute<DisplayAttribute>();
var name = pInfo.Name;

30

Bir Generic Extension Property Attribute Helper yazarak benzer sorunları çözdüm:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

public static class AttributeHelper
{
    public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(
        Expression<Func<T, TOut>> propertyExpression, 
        Func<TAttribute, TValue> valueSelector) 
        where TAttribute : Attribute
    {
        var expression = (MemberExpression) propertyExpression.Body;
        var propertyInfo = (PropertyInfo) expression.Member;
        var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault() as TAttribute;
        return attr != null ? valueSelector(attr) : default(TValue);
    }
}

Kullanımı:

var author = AttributeHelper.GetPropertyAttributeValue<Book, string, AuthorAttribute, string>(prop => prop.Name, attr => attr.Author);
// author = "AuthorName"

1
Const'tan açıklama niteliğini nasıl alabilirim Fields?
Amir

1
Bir hata iletisi alırsınız: Hata 1775 'Namespace.FieldName' öğesine bir örnek başvurusu ile erişilemiyor; bunun yerine bir tür adıyla nitelendirin. Bunu yapmanız gerekirse, 'const' ifadesini 'salt okunur' olarak değiştirmenizi öneririm.
Mikael Engver

1
Dürüst olmak gerekirse, bundan çok daha yararlı bir oyunuz olmalı. Birçok vaka için çok güzel ve yararlı bir cevap.
David Létourneau

1
Teşekkürler @ DavidLétourneau! Kişi sadece umut edebilir. Sanki bu konuda biraz yardımcı oldun.
Mikael Engver

:) Genel yönteminizi kullanarak bir sınıf için tüm özniteliklerin değerine sahip olmanın ve her bir özelliğe özniteliğin değerini atamanın mümkün olduğunu düşünüyor musunuz?
David Létourneau


12

"Bir parametreyi alan öznitelikler için öznitelik adlarını ve parametre değerini listeleyin" anlamına gelirse, .NET CustomAttributeData4.5'te API aracılığıyla bu daha kolaydır :

using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;

public static class Program
{
    static void Main()
    {
        PropertyInfo prop = typeof(Foo).GetProperty("Bar");
        var vals = GetPropertyAttributes(prop);
        // has: DisplayName = "abc", Browsable = false
    }
    public static Dictionary<string, object> GetPropertyAttributes(PropertyInfo property)
    {
        Dictionary<string, object> attribs = new Dictionary<string, object>();
        // look for attributes that takes one constructor argument
        foreach (CustomAttributeData attribData in property.GetCustomAttributesData()) 
        {

            if(attribData.ConstructorArguments.Count == 1)
            {
                string typeName = attribData.Constructor.DeclaringType.Name;
                if (typeName.EndsWith("Attribute")) typeName = typeName.Substring(0, typeName.Length - 9);
                attribs[typeName] = attribData.ConstructorArguments[0].Value;
            }

        }
        return attribs;
    }
}

class Foo
{
    [DisplayName("abc")]
    [Browsable(false)]
    public string Bar { get; set; }
}

3
private static Dictionary<string, string> GetAuthors()
{
    return typeof(Book).GetProperties()
        .SelectMany(prop => prop.GetCustomAttributes())
        .OfType<AuthorAttribute>()
        .ToDictionary(attribute => attribute.Name, attribute => attribute.Name);
}

2

Yukarıda en çok dile getirilen cevaplar kesinlikle işe yararken, bazı durumlarda biraz farklı bir yaklaşım kullanmanızı öneririm.

Sınıfınızda her zaman aynı özniteliğe sahip birden fazla özellik varsa ve bu öznitelikleri bir sözlükte sıralamak istiyorsanız, nasıl yapacağınız aşağıda açıklanmıştır:

var dict = typeof(Book).GetProperties().ToDictionary(p => p.Name, p => p.GetCustomAttributes(typeof(AuthorName), false).Select(a => (AuthorName)a).FirstOrDefault());

Bu hala cast kullanır ancak yalnızca "AuthorName" türünün özel niteliklerini alacağınız için kadronun her zaman çalışmasını sağlar. Yukarıdaki birden fazla Özneniz varsa, yanıtlar somut bir istisna alır.


1
public static class PropertyInfoExtensions
{
    public static TValue GetAttributValue<TAttribute, TValue>(this PropertyInfo prop, Func<TAttribute, TValue> value) where TAttribute : Attribute
    {
        var att = prop.GetCustomAttributes(
            typeof(TAttribute), true
            ).FirstOrDefault() as TAttribute;
        if (att != null)
        {
            return value(att);
        }
        return default(TValue);
    }
}

Kullanımı:

 //get class properties with attribute [AuthorAttribute]
        var props = typeof(Book).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(AuthorAttribute)));
            foreach (var prop in props)
            {
               string value = prop.GetAttributValue((AuthorAttribute a) => a.Name);
            }

veya:

 //get class properties with attribute [AuthorAttribute]
        var props = typeof(Book).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(AuthorAttribute)));
        IList<string> values = props.Select(prop => prop.GetAttributValue((AuthorAttribute a) => a.Name)).Where(attr => attr != null).ToList();

1

MaxLength veya başka bir özelliği almak için kullanabileceğiniz bazı statik yöntemler şunlardır.

using System;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;

public static class AttributeHelpers {

public static Int32 GetMaxLength<T>(Expression<Func<T,string>> propertyExpression) {
    return GetPropertyAttributeValue<T,string,MaxLengthAttribute,Int32>(propertyExpression,attr => attr.Length);
}

//Optional Extension method
public static Int32 GetMaxLength<T>(this T instance,Expression<Func<T,string>> propertyExpression) {
    return GetMaxLength<T>(propertyExpression);
}


//Required generic method to get any property attribute from any class
public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(Expression<Func<T,TOut>> propertyExpression,Func<TAttribute,TValue> valueSelector) where TAttribute : Attribute {
    var expression = (MemberExpression)propertyExpression.Body;
    var propertyInfo = (PropertyInfo)expression.Member;
    var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute),true).FirstOrDefault() as TAttribute;

    if (attr==null) {
        throw new MissingMemberException(typeof(T).Name+"."+propertyInfo.Name,typeof(TAttribute).Name);
    }

    return valueSelector(attr);
}

}

Statik yöntemi kullanarak ...

var length = AttributeHelpers.GetMaxLength<Player>(x => x.PlayerName);

Veya bir örnekte isteğe bağlı uzantı yöntemini kullanarak ...

var player = new Player();
var length = player.GetMaxLength(x => x.PlayerName);

Veya başka bir öznitelik için tam statik yöntemi kullanarak (örneğin StringLength) ...

var length = AttributeHelpers.GetPropertyAttributeValue<Player,string,StringLengthAttribute,Int32>(prop => prop.PlayerName,attr => attr.MaximumLength);

Mikael Engver'in cevabından ilham aldı.


1

Necromancing.
Hala .NET 2.0'ı korumak zorunda olanlar veya LINQ olmadan yapmak isteyenler için:

public static object GetAttribute(System.Reflection.MemberInfo mi, System.Type t)
{
    object[] objs = mi.GetCustomAttributes(t, true);

    if (objs == null || objs.Length < 1)
        return null;

    return objs[0];
}



public static T GetAttribute<T>(System.Reflection.MemberInfo mi)
{
    return (T)GetAttribute(mi, typeof(T));
}


public delegate TResult GetValue_t<in T, out TResult>(T arg1);

public static TValue GetAttributValue<TAttribute, TValue>(System.Reflection.MemberInfo mi, GetValue_t<TAttribute, TValue> value) where TAttribute : System.Attribute
{
    TAttribute[] objAtts = (TAttribute[])mi.GetCustomAttributes(typeof(TAttribute), true);
    TAttribute att = (objAtts == null || objAtts.Length < 1) ? default(TAttribute) : objAtts[0];
    // TAttribute att = (TAttribute)GetAttribute(mi, typeof(TAttribute));

    if (att != null)
    {
        return value(att);
    }
    return default(TValue);
}

Örnek kullanım:

System.Reflection.FieldInfo fi = t.GetField("PrintBackground");
wkHtmlOptionNameAttribute att = GetAttribute<wkHtmlOptionNameAttribute>(fi);
string name = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, delegate(wkHtmlOptionNameAttribute a){ return a.Name;});

ya da sadece

string aname = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, a => a.Name );

0
foreach (var p in model.GetType().GetProperties())
{
   var valueOfDisplay = 
       p.GetCustomAttributesData()
        .Any(a => a.AttributeType.Name == "DisplayNameAttribute") ? 
            p.GetCustomAttribute<DisplayNameAttribute>().DisplayName : 
            p.Name;
}

Bu örnekte, bir değerle gösterilecek 'DisplayName' adlı bir alana sahip olduğu için Author yerine DisplayName kullandım.


0

numaradan öznitelik almak için kullanıyorum:

 public enum ExceptionCodes
 {
  [ExceptionCode(1000)]
  InternalError,
 }

 public static (int code, string message) Translate(ExceptionCodes code)
        {
            return code.GetType()
            .GetField(Enum.GetName(typeof(ExceptionCodes), code))
            .GetCustomAttributes(false).Where((attr) =>
            {
                return (attr is ExceptionCodeAttribute);
            }).Select(customAttr =>
            {
                var attr = (customAttr as ExceptionCodeAttribute);
                return (attr.Code, attr.FriendlyMessage);
            }).FirstOrDefault();
        }

// kullanma

 var _message = Translate(code);

0

Sadece bu kod parçasını koymak için doğru yeri arıyorum.

diyelim ki aşağıdaki mülkünüz var:

[Display(Name = "Solar Radiation (Average)", ShortName = "SolarRadiationAvg")]
public int SolarRadiationAvgSensorId { get; set; }

Ve ShortName değerini almak istiyorsunuz. Yapabilirsin:

((DisplayAttribute)(typeof(SensorsModel).GetProperty(SolarRadiationAvgSensorId).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;

Veya genel yapmak için:

internal static string GetPropertyAttributeShortName(string propertyName)
{
    return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.