2009-02-12 41 views
91

Điều này sẽ có nghĩa là "không". Đó là điều không may.Lớp C# có thể kế thừa các thuộc tính từ giao diện của nó không?

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

Lớp học có thể kế thừa các thuộc tính từ giao diện không? Hay tôi đang sủa cây sai ở đây?

Trả lời

59

Không. Khi thực hiện giao diện hoặc ghi đè thành viên trong lớp dẫn xuất, bạn cần phải khai báo lại thuộc tính.

Nếu bạn chỉ quan tâm đến ComponentModel (không phản ánh trực tiếp), có cách ([AttributeProvider]) đề xuất thuộc tính từ loại hiện có (để tránh trùng lặp), nhưng nó chỉ hợp lệ để sử dụng thuộc tính và chỉ mục.

Như một ví dụ:

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

kết quả đầu ra:

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

Bạn có chắc chắn về điều này không? Phương thức MemberInfo.GetCustomAttributes lấy một đối số cho biết liệu cây kế thừa có nên được tìm kiếm hay không. –

+3

Hmm. Tôi chỉ nhận thấy rằng câu hỏi là về các thuộc tính kế thừa từ một giao diện không phải từ một lớp cơ sở. –

+0

Có lý do gì để đặt thuộc tính trên giao diện không? –

30

Bạn có thể xác định một phương pháp mở rộng hữu ích ...

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

Dưới đây là phương pháp mở rộng:

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

Cập nhật:

Đây là một phiên bản ngắn hơn theo đề nghị của SimonD trong một chú thích:

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

Điều này chỉ nhận thuộc tính cấp loại chứ không phải thuộc tính, trường hoặc thành viên, phải không? – Maslow

+17

rất đẹp, cá nhân tôi sử dụng phiên bản ngắn hơn này, bây giờ: riêng tư IEnumerable GetCustomAttributesIncludingBaseInterfaces (loại này) { var attributeType = typeof (T); kiểu trả về.GetCustomAttributes (attributeType, true) .Union (type.GetInterfaces(). SelectMany (interfaceType => interfaceType.GetCustomAttributes (attributeType, true))) Phân biệt(). Cast (); } –

+1

@SimonD .: Và giải pháp tái cấu trúc của bạn nhanh hơn. – mynkow

16

Một bài viết của Brad Wilson về điều này: Interface Attributes != Class Attributes

Để tóm tắt: lớp don' t kế thừa từ các giao diện, chúng thực hiện chúng. Điều này có nghĩa rằng các thuộc tính không tự động là một phần của việc thực hiện.

Nếu bạn cần kế thừa thuộc tính, hãy sử dụng lớp cơ sở trừu tượng, thay vì giao diện.

10

Trong khi lớp C# không kế thừa các thuộc tính từ giao diện của nó, có một sự thay thế hữu ích khi mô hình liên kết trong ASP.NET MVC3.

Nếu bạn khai báo mô hình của quan điểm là giao diện chứ không phải là loại bê tông, sau đó quan điểm và mô hình chất kết dính sẽ áp dụng các thuộc tính (ví dụ, [Required] hoặc [DisplayName("Foo")] từ giao diện khi vẽ và xác nhận mô hình:

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

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

Sau đó, trong giao diện:.

@* 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

này được nhiều hơn cho những người tìm kiếm để trích xuất các thuộc tính từ tính có thể tồn tại trên một giao diện thực hiện Bởi vì những thuộc tính không phải là một phần của lớp học, điều này sẽ cung cấp cho bạn truy cập vào chúng. lưu ý, tôi có một lớp container đơn giản cung cấp cho bạn quyền truy cập vào PropertyInfo - vì đó là những gì tôi cần nó. Hack lên khi bạn cần. Điều này làm việc tốt cho tôi.

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; } 
    } 
} 
Các vấn đề liên quan