Reflection - dapatkan nama atribut dan nilai pada properti


253

Saya punya kelas, sebut saja Buku dengan properti bernama Nama. Dengan properti itu, saya memiliki atribut yang terkait dengannya.

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

Dalam metode utama saya, saya menggunakan refleksi dan ingin mendapatkan pasangan nilai kunci dari setiap atribut untuk setiap properti. Jadi dalam contoh ini, saya berharap melihat "Penulis" untuk nama atribut dan "Nama Pengarang" untuk nilai atribut.

Pertanyaan: Bagaimana cara saya mendapatkan nama atribut dan nilai properti saya menggunakan Refleksi?


apa yang terjadi ketika Anda mencoba mengakses properti pada objek itu melalui refleksi, apakah Anda terjebak di suatu tempat atau Anda ingin kode untuk refleksi
kobe

Jawaban:


308

Gunakan typeof(Book).GetProperties()untuk mendapatkan berbagai PropertyInfoinstance. Kemudian gunakan GetCustomAttributes()pada masing PropertyInfo- masing untuk melihat apakah ada di antara mereka yang memiliki Authortipe atribut. Jika ya, Anda bisa mendapatkan nama properti dari info properti dan nilai atribut dari atribut tersebut.

Sesuatu di sepanjang baris ini untuk memindai tipe properti yang memiliki tipe atribut spesifik dan untuk mengembalikan data dalam kamus (perhatikan bahwa ini dapat dibuat lebih dinamis dengan mengirimkan tipe ke rutin):

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
Saya berharap bahwa saya tidak perlu membuang atribut.
developerdoug

prop.GetCustomAttributes (true) hanya mengembalikan objek []. Jika Anda tidak ingin melakukan casting maka Anda dapat menggunakan refleksi pada instance atribut itu sendiri.
Adam Markowitz

Apa itu AuthorAttribute di sini? Apakah ini kelas yang berasal dari Atribut? @Adam Markowitz
Sarath Avanavu

1
Iya. OP menggunakan atribut khusus bernama 'Penulis'. Lihat di sini untuk contoh: msdn.microsoft.com/en-us/library/sw480ze8.aspx
Adam Markowitz

1
Biaya kinerja dari pengecoran atribut sama sekali tidak signifikan dibandingkan dengan setiap operasi lain yang terlibat (kecuali pemeriksaan nol dan penugasan string).
SilentSin

112

Untuk mendapatkan semua atribut properti dalam kamus, gunakan ini:

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

ingatlah untuk berubah dari falsemenjadi truejika Anda ingin menyertakan atribut yang diwarisi juga.


3
Ini secara efektif melakukan hal yang sama dengan solusi Adam, tetapi jauh lebih ringkas.
Daniel Moore

31
Tambahkan .OfType <AuthorAttribue> () ke ekspresi alih-alih ToDictionary jika Anda hanya memerlukan atribut Author dan ingin melewatkan pemeran masa depan
Adrian Zanescu

2
Tidakkah ini akan melempar pengecualian ketika ada dua atribut dari tipe yang sama pada properti yang sama?
Konstantin

53

Jika Anda hanya ingin satu nilai Atribut tertentu Misalnya Tampilan Atribut, Anda dapat menggunakan kode berikut:

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

30

Saya telah memecahkan masalah serupa dengan menulis Pembantu Atribut Properti Ekstensi Generik:

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);
    }
}

Pemakaian:

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

1
Bagaimana saya bisa mendapatkan atribut deskripsi dari const Fields?
Amir

1
Anda akan mendapatkan: Kesalahan 1775 Anggota 'Namespace.FieldName' tidak dapat diakses dengan referensi contoh; berkualifikasi dengan nama jenis saja. Jika Anda perlu melakukan ini, saya sarankan untuk mengubah 'const' menjadi 'readonly'.
Mikael Engver

1
Anda harus memiliki suara yang jauh lebih berguna daripada itu, jujur. Ini jawaban yang sangat bagus dan berguna untuk banyak kasus.
David Létourneau

1
Terima kasih @ DavidLétourneau! Orang hanya bisa berharap. Sepertinya Anda sedikit membantu dalam hal itu.
Mikael Engver

:) Apakah menurut Anda dimungkinkan untuk memiliki nilai semua atribut untuk satu kelas dengan menggunakan metode generik Anda dan menetapkan nilai atribut untuk setiap properti?
David Létourneau


12

Jika Anda bermaksud "untuk atribut yang mengambil satu parameter, daftarkan nama-nama atribut dan nilai-parameter", maka ini lebih mudah di .NET 4.5 melalui CustomAttributeDataAPI:

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

Meskipun jawaban di atas yang paling dipilih pasti bekerja, saya sarankan menggunakan pendekatan yang sedikit berbeda dalam beberapa kasus.

Jika kelas Anda memiliki beberapa properti dengan atribut yang selalu sama dan Anda ingin agar atribut tersebut diurutkan ke dalam kamus, berikut caranya:

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

Ini masih menggunakan pemeran tetapi memastikan bahwa pemeran akan selalu bekerja karena Anda hanya akan mendapatkan atribut khusus dari jenis "Nama Pengarang". Jika Anda memiliki beberapa Atribut, jawaban di atas akan mendapatkan pengecualian.


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);
    }
}

Pemakaian:

 //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);
            }

atau:

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

Berikut adalah beberapa metode statis yang dapat Anda gunakan untuk mendapatkan MaxLength, atau atribut lainnya.

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);
}

}

Menggunakan metode statis ...

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

Atau menggunakan metode ekstensi opsional pada contoh ...

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

Atau menggunakan metode statis penuh untuk atribut lain (StringLength misalnya) ...

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

Terinspirasi oleh jawaban Mikael Engver.


1

Necromancing.
Bagi mereka yang masih harus mempertahankan .NET 2.0, atau mereka yang ingin melakukannya tanpa LINQ:

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);
}

Contoh penggunaan:

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

atau sederhana

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;
}

Dalam contoh ini saya menggunakan DisplayName bukan Penulis karena memiliki bidang bernama 'DisplayName' untuk ditampilkan dengan nilai.


0

untuk mendapatkan atribut dari enum, saya menggunakan:

 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();
        }

// Menggunakan

 var _message = Translate(code);

0

Hanya mencari tempat yang tepat untuk meletakkan kode ini.

katakanlah Anda memiliki properti berikut:

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

Dan Anda ingin mendapatkan nilai ShortName. Anda dapat melakukan:

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

Atau untuk membuatnya umum:

internal static string GetPropertyAttributeShortName(string propertyName)
{
    return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.