문제

EDIT: I'd better rephrase: How can I shift the GET-implementation of a Class property to a / using a custom attribute? (I've added instantation vars (classname, propertyname) to the attribute, however I'd rather have these automatically fetched ofcourse.)

Public Class CustomClass
    <CustomAttributeClass(ClassName:="CustomClass", PropertyName = "SomeProperty")> _
    Public Property SomeProperty() as String
        Get() as String
            //This implementation should be handled by the attribute class
        End Get

        Set(Byval value as String)
            Me._someProperty = value
        End Set
    End Property
End Class

Old question:

I want to create a custom property attribute for classes. I can create a class derived from Attribute, and 'mark' the property with the attribute, but where to go from here?

I have a repository where I can quickly get data based on the attributes values. I would like to generalize the behaviour of the property in the attribute but I don't know how to go from here... Any help would be greatly accepted!

Public Class CustomDataAttribute : Inherits Attribute
    Private _name As String

    Public Sub New(ByVal name As String)
        Me.Name = name
    End Sub

    Property Name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            Me._name = value
        End Set
    End Property
End Class


Public Class CustomClass
    <CustomDataAttribute(Name:="CustomField")> _ 
    Public Property CustomField()
    End Property
End Class
도움이 되었습니까?

해결책

You will have to use Reflection to discover the attribute. In your case, you'll get it from PropertyInfo.GetCustomAttributes().

The harder part of using attributes is to find a suitable execution model to actually use them. Something like a compiler, a designer or a class that serializes objects is an obvious one. The usability of attributes quickly spirals down from there. It is almost always the wrong choice when you try to use an attribute where a virtual property is actually needed. Retrieving attribute values is very expensive, many orders of magnitude more expensive than retrieving a property value. Use them only when the reflection code runs at human time (like compilers) or when the cost is insignificant compared to the benefit or the overhead (common in any kind of I/O operation).

다른 팁

From your comments to the previous answer, I think you'll find that .NET attributes aren't quite as flexible as you want them to be.

You asked "Isn't there some base property-attribute which has some events like onGet and onSet?" -- no; attributes have no built-in interaction with their targets, whether methods or classes. In fact, you can't even tell at runtime what the target of an attribute is; you have to know the target (class, method, property etc) first and then query which attributes decorate it.

Secondly, attributes aren't actually created until you query for them. When you call GetCustomAttributes, the runtime system evaluates the assembly metadata and instantiates the attributes that were specified. If you call it twice in a row, you'll get two sets of the same attributes.

Going back to your other question: if you want to know when a property decorated with your attributes is set or retrieved, you'd have to implement INotifyPropertyChanged on all of your related classes, write code to search all your classes for properties marked with that attribute upon assembly load, and then build some interactivity that hooked up the PropertyChanged events to whatever code you need to fire. (And that only notifies you about set operations, not get.)

No idea if this is helpful, but there you go. :-)

Here is a class which helps processing custom attributes when using reflection. Pass the type as a parameter to contructor.

public class AttributeList : List<Attribute>
{
    /// <summary>
    /// Gets a list of custom attributes
    /// </summary>
    /// <param name="propertyInfo"></param>
    /// <returns></returns>
    public static AttributeList GetCustomAttributeList(ICustomAttributeProvider propertyInfo)
    {
        var result = new AttributeList();
        result.AddRange(propertyInfo.GetCustomAttributes(false).Cast<Attribute>());
        return result;
    }

    /// <summary>
    /// Finds attribute in collection by its own type or parents type
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public T FindAttribute<T>() where T : Attribute
    {
        return (T)Find(x => typeof(T).IsAssignableFrom(x.GetType()));
    }

    /// <summary>
    /// Finds attribute in collection by its own type or parents type
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public List<T> FindAllAttributes<T>() where T : Attribute
    {
        return new List<T>(FindAll(x => typeof(T).IsAssignableFrom(x.GetType())).Cast<T>());
    }

    /// <summary>
    /// Finds attribute in collection by its own type or parents type
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public List<T> FindAllAttributes<T>(Type implementsType) where T : Attribute
    {
        return new List<T>(FindAll(x => implementsType.IsAssignableFrom(x.GetType())).Cast<T>());
    }

    public bool IsAttributeSet<T>() where T : Attribute
    {
        return FindAttribute<T>() != null;
    }

    public TValue GetValueFromAttributeOrDefault<TAttr, TValue>(Func<TAttr, TValue> func, TValue defaultValue) 
        where TAttr : Attribute
    {
        var attribute = FindAttribute<TAttr>();
        return attribute == null ? 
            defaultValue : 
            func(attribute);
    }
}

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top