परावर्तन - गुण का नाम और संपत्ति पर मूल्य प्राप्त करें


253

मेरे पास एक वर्ग है, इसे नाम से एक संपत्ति के साथ बुक करने की सुविधा देता है। उस संपत्ति के साथ, मेरे पास एक विशेषता है।

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

मेरी मुख्य विधि में, मैं प्रतिबिंब का उपयोग कर रहा हूं और प्रत्येक संपत्ति के लिए प्रत्येक विशेषता के प्रमुख मूल्य जोड़ी प्राप्त करना चाहता हूं। इसलिए इस उदाहरण में, मुझे विशेषता नाम के लिए "लेखक" और विशेषता मान के लिए "AuthorName" देखने की उम्मीद है।

प्रश्न: मैं प्रतिबिंब का उपयोग करके अपने गुणों पर विशेषता नाम और मूल्य कैसे प्राप्त करूं?


क्या हो रहा है जब आप प्रतिबिंब के माध्यम से उस वस्तु पर संपत्ति का उपयोग करने की कोशिश कर रहे हैं, तो क्या आप कहीं फंस गए हैं या क्या आप प्रतिबिंब के लिए कोड चाहते हैं
कोबे

जवाबों:


307

उदाहरणों typeof(Book).GetProperties()की एक सरणी प्राप्त करने के लिए उपयोग करें PropertyInfo। फिर GetCustomAttributes()प्रत्येक PropertyInfoपर यह देखने के लिए उपयोग करें कि उनमें से किसी के पास Authorएट्रीब्यूट टाइप है या नहीं। यदि वे करते हैं, तो आप संपत्ति की जानकारी से संपत्ति का नाम और विशेषता से विशेषता मान प्राप्त कर सकते हैं।

गुणों के लिए एक प्रकार को स्कैन करने के लिए इन पंक्तियों के साथ कुछ जो एक विशिष्ट विशेषता प्रकार है और एक शब्दकोश में डेटा वापस करने के लिए (ध्यान दें कि इसे दिनचर्या में टाइप करके अधिक गतिशील बनाया जा सकता है):

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
मैं उम्मीद कर रहा था कि मुझे इस विशेषता को कास्ट नहीं करना पड़ेगा।
डेवलपर

Prop.GetCustomAttributes (सत्य) केवल एक वस्तु देता है []। यदि आप कास्ट नहीं करना चाहते हैं, तो आप स्वयं विशेषता उदाहरणों पर प्रतिबिंब का उपयोग कर सकते हैं।
एडम मार्कोविट

यहाँ लेखक क्या है? क्या यह एक वर्ग है जो विशेषता से लिया गया है? @ एडम मार्कोविज
सारथ अवनवु

1
हाँ। ओपी 'ऑथर' नामक एक कस्टम विशेषता का उपयोग कर रहा है। एक उदाहरण के लिए यहाँ देखें: msdn.microsoft.com/en-us/library/sw480ze8.aspx
एडम मार्कोवित्ज़

1
विशेषता को शामिल करने की प्रदर्शन लागत हर दूसरे ऑपरेशन (नल चेक और स्ट्रिंग असाइनमेंट को छोड़कर) की तुलना में पूरी तरह से महत्वहीन है।
साइलेंटसिन

112

एक शब्दकोश में एक संपत्ति के सभी विशेषताओं को प्राप्त करने के लिए इसका उपयोग करें:

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

से बदलने के लिए याद falseकरने के लिए trueआप अच्छी तरह से inheritted विशेषताएं शामिल करना चाहते हैं।


3
यह प्रभावी रूप से एडम के समाधान के समान है, लेकिन कहीं अधिक संक्षिप्त है।
डैनियल मूर

31
परिशिष्ट .OfType <AuthorAttribue> () टोबैडर के बजाय अभिव्यक्ति के लिए (यदि आपको केवल लेखक के गुणों की आवश्यकता है और भविष्य के कलाकार को छोड़ना चाहते हैं
एड्रियन ज़ैनस्क्यू

2
क्या यह अपवाद नहीं होगा जब एक ही संपत्ति पर एक ही प्रकार के दो गुण हों?
कॉन्स्टेंटिन

53

यदि आप उदाहरण के लिए केवल एक विशिष्ट विशेषता मान चाहते हैं, तो प्रदर्शन कोड आप निम्नलिखित कोड का उपयोग कर सकते हैं:

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

30

मैंने जेनेरिक एक्सटेंशन प्रॉपर्टी एट्रीब्यूट हेल्पर लिखकर इसी तरह की समस्याओं को हल किया है:

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

उपयोग:

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

1
कॉन्स्ट से मैं विवरण विशेषता कैसे प्राप्त कर सकता हूं Fields?
आमिर

1
आपको एक मिल जाएगा: त्रुटि 1775 सदस्य 'नामस्थान.फिल्डनाम' को उदाहरण संदर्भ के साथ एक्सेस नहीं किया जा सकता है; इसके बजाय एक प्रकार के नाम के साथ इसे योग्य बनाएं। यदि आपको ऐसा करने की आवश्यकता है, तो मैं 'कास्ट' को 'आसानी से' में बदलने का सुझाव देता हूं।
मिकेल एंगवर

1
आपके पास ईमानदारी से बहुत अधिक उपयोगी वोट होना चाहिए। यह कई मामलों में बहुत अच्छा और उपयोगी उत्तर है।
डेविड लेटरन्यू

1
धन्यवाद @ डेविडलेटन! एक ही उम्मीद कर सकता है। ऐसा लगता है जैसे आपने इसमें थोड़ी मदद की।
मिकेल एंगवर

:) क्या आपको लगता है कि अपनी जेनेरिक पद्धति का उपयोग करके एक वर्ग के लिए सभी विशेषताओं का मूल्य होना और प्रत्येक गुणों के लिए विशेषता का मूल्य निर्दिष्ट करना संभव है?
डेविड लेटरन्यू

21

आप उपयोग कर सकते हैं GetCustomAttributesData()और GetCustomAttributes():

var attributeData = typeof(Book).GetProperty("Name").GetCustomAttributesData();
var attributes = typeof(Book).GetProperty("Name").GetCustomAttributes(false);

4
क्या फर्क पड़ता है?
प्राइम बाय डिजाइन

1
@PrimeByDesign पूर्व में पता लगाया गया है कि लागू विशेषताओं को तत्काल कैसे लागू किया जाए। उत्तरार्द्ध वास्तव में उन विशेषताओं को तात्कालिक बनाता है।
HappyNomad

12

यदि आपका मतलब "उन विशेषताओं के लिए है जो एक पैरामीटर लेते हैं, तो विशेषता-नामों और पैरामीटर-मूल्य को सूचीबद्ध करें", तो यह CustomAttributeDataएपीआई के माध्यम से .NET 4.5 में आसान है :

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

हालांकि सबसे ऊपर के उत्कीर्ण उत्तर निश्चित रूप से काम करते हैं, मैं कुछ मामलों में थोड़ा अलग दृष्टिकोण का उपयोग करने का सुझाव दूंगा।

यदि आपकी कक्षा में हमेशा एक ही विशेषता के साथ कई गुण हैं और आप उन विशेषताओं को एक शब्दकोश में क्रमबद्ध करना चाहते हैं, तो इस प्रकार है:

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

यह अभी भी कलाकारों का उपयोग करता है लेकिन यह सुनिश्चित करता है कि कलाकार हमेशा काम करेगा क्योंकि आपको केवल "AuthorName" प्रकार के कस्टम गुण प्राप्त होंगे। यदि आपके पास उत्तर से ऊपर कई गुण हैं, तो एक कास्ट अपवाद मिलेगा।


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

उपयोग:

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

या:

 //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, या किसी अन्य विशेषता को प्राप्त करने के लिए कर सकते हैं।

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

}

स्थैतिक विधि का उपयोग ...

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

या एक उदाहरण पर वैकल्पिक विस्तार विधि का उपयोग कर ...

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

या किसी अन्य विशेषता के लिए पूर्ण स्थैतिक विधि का उपयोग करना (उदाहरण के लिए StringLength) ...

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

मिकेल एंगवर के जवाब से प्रेरित।


1

Necromancing।
उन लोगों के लिए जो अभी भी .NET 2.0 को बनाए रखना चाहते हैं, या जो 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);
}

उदाहरण का उपयोग:

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

या केवल

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

इस उदाहरण में मैंने लेखक के बजाय DisplayName का उपयोग किया क्योंकि इसमें 'DisplayName' नाम का एक क्षेत्र है जिसे एक मूल्य के साथ दिखाया जाना है।


0

Enum से विशेषता प्राप्त करने के लिए, मैं का उपयोग कर रहा हूँ:

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

// का उपयोग करना

 var _message = Translate(code);

0

कोड के इस टुकड़े को डालने के लिए बस सही जगह की तलाश है।

मान लीजिए कि आपके पास निम्नलिखित संपत्ति है:

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

और आप ShortName मान प्राप्त करना चाहते हैं। तुम कर सकते हो:

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

या इसे सामान्य बनाने के लिए:

internal static string GetPropertyAttributeShortName(string propertyName)
{
    return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.