C#类是否可以从其接口inheritance属性?
这似乎意味着“不”。 这是不幸的。
[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); } }
类可以inheritance接口的属性吗? 还是我在这里叫错了树?
不需要。只要在派生类中实现接口或重写成员,就需要重新声明属性。
如果您只关心ComponentModel(不是直接reflection),则有一种方法( [AttributeProvider]
)从现有types中build议属性(以避免重复),但仅适用于属性和索引器的使用。
举个例子:
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<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 ); } }
更新:
下面是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>(); }
Brad Wilson关于此的一篇文章: Interface Attributes!= Class Attributes
总结一下:类不从接口inheritance,它们实现它们。 这意味着这些属性不会自动成为实现的一部分。
如果您需要inheritance属性,请使用抽象基类,而不是接口。
虽然C#类不从其接口inheritance属性,但在ASP.NET MVC3中绑定模型时有一个有用的select。
如果将视图的模型声明为接口而不是具体types,则视图和模型联编程序将在渲染和validation时从接口应用属性(例如[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)
对于那些希望从实现的接口上可能存在的属性中提取属性的人来说更是如此。 因为这些属性不是类的一部分,这将使您访问它们。 请注意,我有一个简单的容器类,使您可以访问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; } } }