반영-속성의 속성 이름 및 값 가져 오기


253

수업이 있는데 이름이라는 속성으로 Book이라고 부릅니다. 해당 속성과 관련된 속성이 있습니다.

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

내 주요 방법에서 리플렉션을 사용하고 있으며 각 속성에 대한 각 속성의 키 값 쌍을 가져 오려고합니다. 따라서이 예에서는 속성 이름으로 "Author"가 표시되고 속성 값으로 "AuthorName"이 표시됩니다.

질문 : Reflection을 사용하여 속성의 속성 이름과 값을 얻으려면 어떻게합니까?


리플렉션을 통해 해당 객체의 속성에 액세스하려고 할 때 발생하는 상황, 어딘가에 붙어 있거나 리플렉션을위한 코드를 원하십니까
kobe

답변:


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
나는 속성을 캐스팅 할 필요가 없기를 바랐다.
developerdoug

prop.GetCustomAttributes (true)는 객체 [] 만 반환합니다. 캐스트하지 않으려면 속성 인스턴스 자체에 리플렉션을 사용할 수 있습니다.
Adam Markowitz

AuthorAttribute 란 무엇입니까? Attribute에서 파생 된 클래스입니까? @Adam Markowitz
Sarath Avanavu

1
예. OP가 'Author'라는 사용자 정의 속성을 사용하고 있습니다. 예를 들어 여기를 참조하십시오 : msdn.microsoft.com/en-us/library/sw480ze8.aspx
아담 마코 위츠에게

1
속성을 캐스팅하는 데 드는 성능 비용은 관련된 다른 모든 작업 (널 체크 및 문자열 할당 제외)과 비교할 때 전혀 중요하지 않습니다.
SilentSin

112

사전에서 속성의 모든 속성을 얻으려면 다음을 사용하십시오.

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

에서 변경해야 false하는 true당신이 아니라 inheritted 특성을 포함 할 경우.


3
이것은 효과적으로 Adam의 솔루션과 동일하지만 훨씬 간결합니다.
Daniel Moore

31
Author 속성 만 필요하고 향후 캐스트를 건너 뛰려면 ToDictionary 대신 표현식에 .OfType <AuthorAttribue> ()을 추가하십시오.
Adrian Zanescu

2
동일한 속성에 동일한 유형의 속성이 두 개있을 때 예외가 발생하지 않습니까?
Konstantin

53

하나의 특정 속성 값만 원하면 디스플레이 속성과 같이 다음 코드를 사용할 수 있습니다.

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

30

Generic Extension Property Attribute Helper를 작성하여 비슷한 문제를 해결했습니다.

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
const에서 description 속성을 어떻게 얻을 수 Fields있습니까?
Amir

1
오류 1775 멤버 'Namespace.FieldName'에 인스턴스 참조로 액세스 할 수 없습니다. 대신 형식 이름으로 한정하십시오. 이 작업을 수행해야하는 경우 'const'를 'readonly'로 변경하는 것이 좋습니다.
Mikael Engver

1
솔직히 그보다 훨씬 유용한 투표권이 있어야합니다. 많은 경우에 매우 훌륭하고 유용한 답변입니다.
David Létourneau

1
감사합니다 @ DavidLétourneau! 하나만 희망 할 수 있습니다. 당신이 그것에 약간 도움이 된 것처럼 보입니다.
Mikael Engver

:) 일반적인 방법을 사용하여 한 클래스의 모든 속성 값을 가질 수 있고 각 속성에 속성 값을 할당 할 수 있다고 생각하십니까?
David Létourneau

21

당신은 사용할 수 있습니다 GetCustomAttributesData()GetCustomAttributes():

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

4
차이점이 뭐야?
Prime By Design

1
@PrimeByDesign 전자는 적용된 속성을 인스턴스화하는 방법 을 찾습니다 . 후자는 실제로 이러한 속성을 인스턴스화합니다.
HappyNomad

12

"하나의 매개 변수를 사용하는 속성의 경우 attribute-names 및 parameter-value를 나열하십시오"를 의미하는 경우 CustomAttributeDataAPI 를 통해 .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);

Mikael Engver의 답변에서 영감을 얻었습니다.


1

괴롭힘.
.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'이라는 필드가 있으므로 Author 대신 DisplayName을 사용했습니다.


0

열거 형에서 속성을 얻으려면 다음을 사용하고 있습니다.

 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;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.