2009-02-12 3 views
91

Это означает, что это означает «нет». К сожалению.Может ли класс C# наследовать атрибуты из своего интерфейса?

[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class, 
AllowMultiple = true, Inherited = true)] 
public class CustomDescriptionAttribute : Attribute 
{ 
    public string Description { get; private set; } 

    public CustomDescriptionAttribute(string description) 
    { 
     Description = description; 
    } 
} 

[CustomDescription("IProjectController")] 
public interface IProjectController 
{ 
    void Create(string projectName); 
} 

internal class ProjectController : IProjectController 
{ 
    public void Create(string projectName) 
    { 
    } 
} 

[TestFixture] 
public class CustomDescriptionAttributeTests 
{ 
    [Test] 
    public void ProjectController_ShouldHaveCustomDescriptionAttribute() 
    { 
     Type type = typeof(ProjectController); 
     object[] attributes = type.GetCustomAttributes(
      typeof(CustomDescriptionAttribute), 
      true); 

     // NUnit.Framework.AssertionException: Expected: 1 But was: 0 
     Assert.AreEqual(1, attributes.Length); 
    } 
} 

Может ли класс наследовать атрибуты из интерфейса? Или я лаяю здесь неправильное дерево?

ответ

59

Нет. При реализации интерфейса или переопределения элементов в производном классе вам необходимо повторно объявить атрибуты.

Если вы только заботитесь о ComponentModel (не прямое отражение), существует способ ([AttributeProvider]) предложить атрибуты существующего типа (во избежание дублирования), но он действителен только для использования свойств и индексатора.

В качестве примера:

using System; 
using System.ComponentModel; 
class Foo { 
    [AttributeProvider(typeof(IListSource))] 
    public object Bar { get; set; } 

    static void Main() { 
     var bar = TypeDescriptor.GetProperties(typeof(Foo))["Bar"]; 
     foreach (Attribute attrib in bar.Attributes) { 
      Console.WriteLine(attrib); 
     } 
    } 
} 

выходы:

System.SerializableAttribute 
System.ComponentModel.AttributeProviderAttribute 
System.ComponentModel.EditorAttribute 
System.Runtime.InteropServices.ComVisibleAttribute 
System.Runtime.InteropServices.ClassInterfaceAttribute 
System.ComponentModel.TypeConverterAttribute 
System.ComponentModel.MergablePropertyAttribute 
+0

Уверены ли вы в этом? Метод MemberInfo.GetCustomAttributes принимает аргумент, указывающий, нужно ли искать дерево наследования. –

+3

Хм. Я только заметил, что вопрос заключается в наследовании атрибутов из интерфейса не из базового класса. –

+0

Есть ли причина поместить атрибуты на интерфейсы тогда? –

30

Вы можете определить полезный метод расширения ...

Type type = typeof(ProjectController); 
var attributes = type.GetCustomAttributes<CustomDescriptionAttribute>(true); 

Вот метод расширения:

/// <summary>Searches and returns attributes. The inheritance chain is not used to find the attributes.</summary> 
/// <typeparam name="T">The type of attribute to search for.</typeparam> 
/// <param name="type">The type which is searched for the attributes.</param> 
/// <returns>Returns all attributes.</returns> 
public static T[] GetCustomAttributes<T>(this Type type) where T : Attribute 
{ 
    return GetCustomAttributes(type, typeof(T), false).Select(arg => (T)arg).ToArray(); 
} 

/// <summary>Searches and returns attributes.</summary> 
/// <typeparam name="T">The type of attribute to search for.</typeparam> 
/// <param name="type">The type which is searched for the attributes.</param> 
/// <param name="inherit">Specifies whether to search this member's inheritance chain to find the attributes. Interfaces will be searched, too.</param> 
/// <returns>Returns all attributes.</returns> 
public static T[] GetCustomAttributes<T>(this Type type, bool inherit) where T : Attribute 
{ 
    return GetCustomAttributes(type, typeof(T), inherit).Select(arg => (T)arg).ToArray(); 
} 

/// <summary>Private helper for searching attributes.</summary> 
/// <param name="type">The type which is searched for the attribute.</param> 
/// <param name="attributeType">The type of attribute to search for.</param> 
/// <param name="inherit">Specifies whether to search this member's inheritance chain to find the attribute. Interfaces will be searched, too.</param> 
/// <returns>An array that contains all the custom attributes, or an array with zero elements if no attributes are defined.</returns> 
private static object[] GetCustomAttributes(Type type, Type attributeType, bool inherit) 
{ 
    if(!inherit) 
    { 
    return type.GetCustomAttributes(attributeType, false); 
    } 

    var attributeCollection = new Collection<object>(); 
    var baseType = type; 

    do 
    { 
    baseType.GetCustomAttributes(attributeType, true).Apply(attributeCollection.Add); 
    baseType = baseType.BaseType; 
    } 
    while(baseType != null); 

    foreach(var interfaceType in type.GetInterfaces()) 
    { 
    GetCustomAttributes(interfaceType, attributeType, true).Apply(attributeCollection.Add); 
    } 

    var attributeArray = new object[attributeCollection.Count]; 
    attributeCollection.CopyTo(attributeArray, 0); 
    return attributeArray; 
} 

/// <summary>Applies a function to every element of the list.</summary> 
private static void Apply<T>(this IEnumerable<T> enumerable, Action<T> function) 
{ 
    foreach(var item in enumerable) 
    { 
    function.Invoke(item); 
    } 
} 

Update:

Вот сокращенный вариант, предложенный Simond в комментарии:

private static IEnumerable<T> GetCustomAttributesIncludingBaseInterfaces<T>(this Type type) 
{ 
    var attributeType = typeof(T); 
    return type.GetCustomAttributes(attributeType, true). 
    Union(type.GetInterfaces(). 
    SelectMany(interfaceType => interfaceType.GetCustomAttributes(attributeType, true))). 
    Distinct().Cast<T>(); 
} 
+1

Это получает только атрибуты типа, а не свойства, поля или элементы, верно? – Maslow

+17

очень приятно, я лично использую более короткую версию этого, в настоящее время: частные статические IEnumerable GetCustomAttributesIncludingBaseInterfaces (этот тип тип) { уаг AttributeType = TypeOf (T); return type.GetCustomAttributes (attributeType, true) .Union (type.GetInterfaces(). SelectMany (interfaceType => interfaceType.GetCustomAttributes (attributeType, true))). Distinct(). Cast (); } –

+1

@SimonD .: И ваше реорганизованное решение выполняется быстрее. – mynkow

16

В статье Брэд Уилсон об этом: Interface Attributes != Class Attributes

Резюмируя: классы Дон» t наследуют от интерфейсов, они реализуют их. Это означает, что атрибуты не являются автоматически частью реализации.

Если вам нужно наследовать атрибуты, используйте абстрактный базовый класс, а не интерфейс.

10

Хотя класс C# не наследует атрибуты от его интерфейсов, есть полезная альтернатива при привязке моделей в ASP.NET MVC3.

Если объявить модель мнения, чтобы быть интерфейсом, а не конкретный типа, то вид и модель связующим будет применять атрибуты (например, [Required] или [DisplayName("Foo")] из интерфейса при визуализации и проверка модели:

public interface IModel { 
    [Required] 
    [DisplayName("Foo Bar")] 
    string FooBar { get; set; } 
} 

public class Model : IModel { 
    public string FooBar { get; set; } 
} 

Тогда в представлении:.

@* Note use of interface type for the view model *@ 
@model IModel 

@* This control will receive the attributes from the interface *@ 
@Html.EditorFor(m => m.FooBar) 
2

Это больше для тех, кто хочет, чтобы извлечь атрибуты из свойств, которые могут существовать на реализованный интерфейс Поскольку эти атрибуты не являются частью класс, это даст вам доступ к ним. Заметьте, у меня есть простой класс контейнера, который дает вам доступ к PropertyInfo - для этого я и нуждался. Взломайте, как вам нужно. Это сработало для меня.

public static class CustomAttributeExtractorExtensions 
{ 
    /// <summary> 
    /// Extraction of property attributes as well as attributes on implemented interfaces. 
    /// This will walk up recursive to collect any interface attribute as well as their parent interfaces. 
    /// </summary> 
    /// <typeparam name="TAttributeType"></typeparam> 
    /// <param name="typeToReflect"></param> 
    /// <returns></returns> 
    public static List<PropertyAttributeContainer<TAttributeType>> GetPropertyAttributesFromType<TAttributeType>(this Type typeToReflect) 
     where TAttributeType : Attribute 
    { 
     var list = new List<PropertyAttributeContainer<TAttributeType>>(); 

     // Loop over the direct property members 
     var properties = typeToReflect.GetProperties(); 

     foreach (var propertyInfo in properties) 
     { 
      // Get the attributes as well as from the inherited classes (true) 
      var attributes = propertyInfo.GetCustomAttributes<TAttributeType>(true).ToList(); 
      if (!attributes.Any()) continue; 

      list.AddRange(attributes.Select(attr => new PropertyAttributeContainer<TAttributeType>(attr, propertyInfo))); 
     } 

     // Look at the type interface declarations and extract from that type. 
     var interfaces = typeToReflect.GetInterfaces(); 

     foreach (var @interface in interfaces) 
     { 
      list.AddRange(@interface.GetPropertyAttributesFromType<TAttributeType>()); 
     } 

     return list; 

    } 

    /// <summary> 
    /// Simple container for the Property and Attribute used. Handy if you want refrence to the original property. 
    /// </summary> 
    /// <typeparam name="TAttributeType"></typeparam> 
    public class PropertyAttributeContainer<TAttributeType> 
    { 
     internal PropertyAttributeContainer(TAttributeType attribute, PropertyInfo property) 
     { 
      Property = property; 
      Attribute = attribute; 
     } 

     public PropertyInfo Property { get; private set; } 

     public TAttributeType Attribute { get; private set; } 
    } 
}