Can a C# class inherit attributes from its interface?
No. Whenever implementing an interface or overriding members in a derived class, you need to re-declare the attributes.
If you only care about ComponentModel (not direct reflection), there is a way ([AttributeProvider]
) of suggesting attributes from an existing type (to avoid duplication), but it is only valid for property and indexer usage.
As an example:
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);
}
}
}
outputs:
System.SerializableAttribute
System.ComponentModel.AttributeProviderAttribute
System.ComponentModel.EditorAttribute
System.Runtime.InteropServices.ComVisibleAttribute
System.Runtime.InteropServices.ClassInterfaceAttribute
System.ComponentModel.TypeConverterAttribute
System.ComponentModel.MergablePropertyAttribute
You can define a useful extension method ...
Type type = typeof(ProjectController);
var attributes = type.GetCustomAttributes<CustomDescriptionAttribute>( true );
Here is the extension method:
/// <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:
Here is a shorter version as proposed by SimonD in a comment:
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)))
.Cast<T>();
}
An article by Brad Wilson about this: Interface Attributes != Class Attributes
To summarise: classes don't inherit from interfaces, they implement them. This means that the attributes are not automatically part of the implementation.
If you need to inherit attributes, use an abstract base class, rather than an interface.
While a C# class does not inherit attributes from its interfaces, there is a useful alternative when binding models in ASP.NET MVC3.
If you declare the view's model to be the interface rather than the concrete type, then the view and the model binder will apply the attributes (e.g., [Required]
or [DisplayName("Foo")]
from the interface when rendering and validating the model:
public interface IModel {
[Required]
[DisplayName("Foo Bar")]
string FooBar { get; set; }
}
public class Model : IModel {
public string FooBar { get; set; }
}
Then in the view:
@* 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)