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?

dikembangkan
sumber
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;
}
Adam Markowitz
sumber
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.

Mo Valipour
sumber
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;
maxspan
sumber
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"
Mikael Engver
sumber
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
21

Anda dapat menggunakan GetCustomAttributesData()dan GetCustomAttributes():

var attributeData = typeof(Book).GetProperty("Name").GetCustomAttributesData();
var attributes = typeof(Book).GetProperty("Name").GetCustomAttributes(false);
Gelas pecah
sumber
4
apa bedanya?
Prime By Design
1
@PrimeByDesign Yang pertama mengetahui bagaimana membuat instantiate atribut yang diterapkan. Yang terakhir sebenarnya instantiate atribut tersebut.
HappyNomad
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; }
}
Marc Gravell
sumber
3
private static Dictionary<string, string> GetAuthors()
{
    return typeof(Book).GetProperties()
        .SelectMany(prop => prop.GetCustomAttributes())
        .OfType<AuthorAttribute>()
        .ToDictionary(attribute => attribute.Name, attribute => attribute.Name);
}
Daniel Dušek
sumber
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.

Mirko Brandt
sumber
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();
Pemenang
sumber
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.

Carter Medlin
sumber
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 );
Stefan Steiger
sumber
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.

petrosmm
sumber
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);
Mohamed.Abdo
sumber
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;
}
Asaf
sumber