属性上的自定义属性 - 获取带属性的属性的类型和值

39
我有以下自定义属性,可以用于属性:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class IdentifierAttribute : Attribute
{
}

例如:

public class MyClass
{
    [Identifier()]
    public string Name { get; set; }

    public int SomeNumber { get; set; }
    public string SomeOtherProperty { get; set; }
}

还会有其他类,可以将Identifier属性添加到不同类型的属性中:

public class MyOtherClass
{
    public string Name { get; set; }

    [Identifier()]
    public int SomeNumber { get; set; }

    public string SomeOtherProperty { get; set; }
}

我需要能够在我的消费类中获取这些信息。 例如:

public class TestClass<T>
{
    public void GetIDForPassedInObject(T obj)
    {
        var type = obj.GetType();
        //type.GetCustomAttributes(true)???
    }
}

如何最好地处理这个问题? 我需要获取[Identifier()]字段的类型(int、string等)和实际值,显然是基于类型。

5个回答

54

类似以下这样的内容,它将只使用第一个具有该属性的属性,当然您可以将其放置在多个属性上。

    public object GetIDForPassedInObject(T obj)
    {
        var prop = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                   .FirstOrDefault(p => p.GetCustomAttributes(typeof(IdentifierAttribute), false).Count() ==1);
        object ret = prop !=null ?  prop.GetValue(obj, null) : null;

        return ret;
    }  

谢谢 - 在FirstOrDefault中无法在lambda内使用“prop”,但我已经解决了 :-) - Alex
啊,是的,我在记事本上写的;-) 已修复。 - Richard Friend
1
我已经使用[Identifier()]标记了我的属性,但是.GetProperties()方法返回除此之外的所有其他属性?我的属性似乎被隐藏了吗? - Alex
尝试指定绑定标志,例如GetProperties(BindingFlags.Public | BindingFlags.Instance); - Richard Friend

3

有点晚,但是这里是我为枚举(也可以是任何对象)编写的一个扩展,用于获取描述属性值(这可以是适用于任何属性的通用扩展):

public enum TransactionTypeEnum
{
    [Description("Text here!")]
    DROP = 1,

    [Description("More text here!")]
    PICKUP = 2,

    ...
}

获取值:

var code = TransactionTypeEnum.DROP.ToCode();

支持我所有枚举类型的扩展:

public static string ToCode(this TransactionTypeEnum val)
{
    return GetCode(val);
}

public static string ToCode(this DockStatusEnum val)
{
    return GetCode(val);
}

public static string ToCode(this TrailerStatusEnum val)
{
    return GetCode(val);
}

public static string ToCode(this DockTrailerStatusEnum val)
{
    return GetCode(val);
}

public static string ToCode(this EncodingType val)
{
    return GetCode(val);
}

private static string GetCode(object val)
{
    var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);

    return attributes.Length > 0 ? attributes[0].Description : string.Empty;
}

你知道在自定义属性类内部访问属性的类型的方法吗? - Aaron

3
public class TestClass<T>
{
    public void GetIDForPassedInObject(T obj)
    {
        PropertyInfo[] properties =
            obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);            

        PropertyInfo IdProperty = (from PropertyInfo property in properties
                           where property.GetCustomAttributes(typeof(Identifier), true).Length > 0
                           select property).First();

         if(null == IdProperty)
             throw new ArgumentException("obj does not have Identifier.");

         Object propValue = IdProperty.GetValue(entity, null)
    }
}

0
我已经在你的示例中添加了一个接受参数并作用于一个属性的属性。
[AttributeUsage(AttributeTargets.Property,AllowMultiple = true)]
public class PersonAttribute : Attribute
{
    public readonly string FieldName;
    public readonly string FieldType;
    public PersonAttribute(string name,string type)
    {
        FieldName = name;
        FieldType = type;
    }    
}

该属性应用于Person类中的应用。
public class Person
{
    public string FirstName {get;set;} = "FirstName";
    public string LastName {get;set;} = "LastName";
    
    [Person("addressline1","db")]
    [Person("new_address1","system")]
    public string AddressLine1 {get;set;} = "Banglore";
    
    [Person("addressline2","system")]
    public string AddressLine2 {get;set;} = "Karnataka";
    
    [Person("addressline3","dto")]
    public string AddressLine3 {get;set;} = "INDIA";    
}


解决方案

我创建了以下通用的扩展函数,它能够读取类中的所有属性和属性值。

public static class AttributesExt
{
    public static IEnumerable<PropertyInfo> AllAttributes<T>(this object obj,string name)
    {
        var allProperties = obj.GetType().GetProperties()
        .Where(_ => _.GetCustomAttributes(typeof(T), true).Length >= 1  && _.Name == name);
        return allProperties;
    }
    
    public static IEnumerable<PropertyInfo> AllAttributes<T>(this object obj)
    {
        var allProperties = obj.GetType().GetProperties()
        .Where(_ => _.GetCustomAttributes(typeof(T), true).Length >= 1);
        return allProperties;
    }

    public static T ReadAttribute<T>(this PropertyInfo propertyInfo)
    {
        var returnType = propertyInfo.GetCustomAttributes(typeof(T), true)
        .Cast<T>().FirstOrDefault();
        return returnType;
    }
}

所以现在在主方法中,如果我们写下以下代码
void Main()
{
    Person p = new Person();
    
    var all = p.AllAttributes<PersonAttribute>().Dump(); //Get All custom attributes
    p.AllAttributes<PersonAttribute>("AddressLine3").Dump();
    all.First(_=> _.Name == "AddressLine2").ReadAttribute<PersonAttribute>().Dump();
    all.First(_=> _.Name == "AddressLine2").ReadAttribute<PersonAttribute>().FieldName.Dump();
}

我们可以根据下面的截图读取这些值。

enter image description here


-1

这里有一个更真实的例子。我们使用扩展方法并检查属性是否包含FieldMetaDataAttribute(我的源代码库中的自定义属性)和有效的Major和MinorVersion。一般感兴趣的是我们使用父类类型和GetProperties来检索ProperyInfo,然后在这种特殊情况下使用GetCustomAttribute来检索属性FieldMetaDataAttribute的部分。使用此代码作为灵感,以更通用的方式检索自定义属性。当然,可以对其进行优化,以创建检索类实例的任何属性的给定属性的通用方法。

        /// <summary>
    /// Executes the action if not the field is deprecated 
    /// </summary>
    /// <typeparam name="TProperty"></typeparam>
    /// <typeparam name="TForm"></typeparam>
    /// <param name="form"></param>
    /// <param name="memberExpression"></param>
    /// <param name="actionToPerform"></param>
    /// <returns>True if the action was performed</returns>
    public static bool ExecuteActionIfNotDeprecated<TForm, TProperty>(this TForm form, Expression<Func<TForm, TProperty>> memberExpression, Action actionToPerform)
    {
        var memberExpressionConverted = memberExpression.Body as MemberExpression;
        if (memberExpressionConverted == null)
            return false; 

        string memberName = memberExpressionConverted.Member.Name;


        PropertyInfo matchingProperty = typeof(TForm).GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .FirstOrDefault(p => p.Name == memberName);
        if (matchingProperty == null)
            return false; //should not occur

        var fieldMeta = matchingProperty.GetCustomAttribute(typeof(FieldMetadataAttribute), true) as FieldMetadataAttribute;
        if (fieldMeta == null)
        {
            actionToPerform();
            return true;
        }

        var formConverted = form as FormDataContract;
        if (formConverted == null)
            return false;

        if (fieldMeta.DeprecatedFromMajorVersion > 0 && formConverted.MajorVersion > fieldMeta.DeprecatedFromMajorVersion)
        {
            //major version of formConverted is deprecated for this field - do not execute action
            return false;
        }

        if (fieldMeta.DeprecatedFromMinorVersion > 0 && fieldMeta.DeprecatedFromMajorVersion > 0
                                                     && formConverted.MinorVersion >= fieldMeta.DeprecatedFromMinorVersion
                                                     && formConverted.MajorVersion >= fieldMeta.DeprecatedFromMajorVersion)
            return false; //the field is expired - do not invoke action 
        actionToPerform();
        return true;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接