C#类可以从其接口继承属性吗?

Rog*_*mbe 110 c# attributes

这似乎意味着"不".哪个是不幸的.

[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);
    }
}
Run Code Online (Sandbox Code Playgroud)

类可以从接口继承属性吗?或者我在这里咆哮错误的树?

Mar*_*ell 67

不会.无论何时在派生类中实现接口或覆盖成员,都需要重新声明属性.

如果您只关心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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

System.SerializableAttribute
System.ComponentModel.AttributeProviderAttribute
System.ComponentModel.EditorAttribute
System.Runtime.InteropServices.ComVisibleAttribute
System.Runtime.InteropServices.ClassInterfaceAttribute
System.ComponentModel.TypeConverterAttribute
System.ComponentModel.MergablePropertyAttribute
Run Code Online (Sandbox Code Playgroud)

  • @Ryan - 确定:用于描述界面.例如,服务合同. (5认同)
  • 嗯.我刚刚注意到问题是从接口继承属性而不是从基类继承属性. (3认同)
  • Marc(和@Rune):是的,OP是关于接口的.但你的答案的第一句可能会令人困惑:"......或派生类中的成员......" - 这不一定是真的.您可以让您的类从其基类继承属性.你只能用接口做到这一点.另见:http://stackoverflow.com/questions/12106566/attribute-on-interface-members-does-not-work (3认同)

tan*_*ius 37

您可以定义一个有用的扩展方法......

Type type = typeof(ProjectController);
var attributes = type.GetCustomAttributes<CustomDescriptionAttribute>( true );
Run Code Online (Sandbox Code Playgroud)

这是扩展方法:

/// <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 );
  }
}
Run Code Online (Sandbox Code Playgroud)

更新:

以下是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>();
}
Run Code Online (Sandbox Code Playgroud)

  • 非常好,我个人使用这个的较短版本,现在:private static IEnumerable <T> GetCustomAttributesIncludingBaseInterfaces <T>(此Type类型){var attributeType = typeof(T); return type.GetCustomAttributes(attributeType,true).Union(type.GetInterfaces().SelectMany(interfaceType => interfaceType.GetCustomAttributes(attributeType,true))).Distinct().Cast <T>(); } (20认同)

Rog*_*mbe 25

Brad Wilson关于此的文章:接口属性!=类属性

总结一下:类不从接口继承,它们实现它们.这意味着属性不会自动成为实现的一部分.

如果需要继承属性,请使用抽象基类,而不是接口.

  • 如果您正在实现多个接口怎么办?您不能只是将这些接口更改为抽象类,因为 C# 缺乏多重继承类别。 (2认同)

Pet*_*uck 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; }
}
Run Code Online (Sandbox Code Playgroud)

然后在视图中:

@* 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)
Run Code Online (Sandbox Code Playgroud)