这似乎意味着"不".哪个是不幸的.
[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); } }
类可以从接口继承属性吗?或者我在这里咆哮错误的树?
不会.无论何时在派生类中实现接口或覆盖成员,都需要重新声明属性.
如果您只关心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
您可以定义一个有用的扩展方法......
Type type = typeof(ProjectController); var attributes = type.GetCustomAttributes( true );
这是扩展方法:
///Searches and returns attributes. The inheritance chain is not used to find the attributes. ///The type of attribute to search for. /// The type which is searched for the attributes. ///Returns all attributes. public static T[] GetCustomAttributes( this Type type ) where T : Attribute { return GetCustomAttributes( type, typeof( T ), false ).Select( arg => (T)arg ).ToArray(); } /// Searches and returns attributes. ///The type of attribute to search for. /// The type which is searched for the attributes. /// Specifies whether to search this member's inheritance chain to find the attributes. Interfaces will be searched, too. ///Returns all attributes. public static T[] GetCustomAttributes( this Type type, bool inherit ) where T : Attribute { return GetCustomAttributes( type, typeof( T ), inherit ).Select( arg => (T)arg ).ToArray(); } /// Private helper for searching attributes. /// The type which is searched for the attribute. /// The type of attribute to search for. /// Specifies whether to search this member's inheritance chain to find the attribute. Interfaces will be searched, too. ///An array that contains all the custom attributes, or an array with zero elements if no attributes are defined. private static object[] GetCustomAttributes( Type type, Type attributeType, bool inherit ) { if( !inherit ) { return type.GetCustomAttributes( attributeType, false ); } var attributeCollection = new Collection
更新:
以下是SimonD在评论中提出的较短版本:
private static IEnumerableGetCustomAttributesIncludingBaseInterfaces (this Type type) { var attributeType = typeof(T); return type.GetCustomAttributes(attributeType, true). Union(type.GetInterfaces(). SelectMany(interfaceType => interfaceType.GetCustomAttributes(attributeType, true))). Distinct().Cast (); }
Brad Wilson关于此的文章:接口属性!=类属性
总结一下:类不从接口继承,它们实现它们.这意味着属性不会自动成为实现的一部分.
如果需要继承属性,请使用抽象基类,而不是接口.
虽然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)