119

これは「いいえ」を暗示しているように見えます。これは残念です。

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

クラスはインターフェイスから属性を継承できますか? それとも、ここで間違ったツリーを吠えていますか?

4

8 に答える 8

76

いいえ。インターフェイスを実装するか、派生クラスでメンバーをオーバーライドするときはいつでも、属性を再宣言する必要があります。

(直接反映ではなく) 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
于 2009-02-12T10:40:04.930 に答える
41

便利な拡張メソッドを定義できます...

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)))
    .Cast<T>();
}
于 2009-06-17T16:30:21.947 に答える
31

これに関するBradWilsonの記事:インターフェイス属性!=クラス属性

要約すると、クラスはインターフェイスから継承せず、実装します。これは、属性が自動的に実装の一部にならないことを意味します。

属性を継承する必要がある場合は、インターフェースではなく、抽象基本クラスを使用してください。

于 2012-05-26T10:13:02.820 に答える
11

C#クラスはそのインターフェイスから属性を継承しませんが、ASP.NETMVC3でモデルをバインドするときに便利な代替手段があります。

ビューのモデルを具象型ではなくインターフェースとして宣言すると、ビューとモデルバインダーは属性を適用します(たとえば、モデルをレンダリング[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)
于 2012-12-03T00:13:46.437 に答える
4

これは、実装されたインターフェイスに存在する可能性のあるプロパティから属性を抽出しようとしている人向けです。これらの属性はクラスの一部ではないため、これによりアクセスできるようになります。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; }
    }
}
于 2016-09-05T19:52:25.610 に答える
0

クラスが持つ同じプロパティにアタッチされた属性/カスタム属性を持つプロパティを持つインターフェイスを追加します。Visual Studio のリファクタリング機能を使用して、クラスのインターフェイスを抽出できます。部分クラスにそのインターフェイスを実装させます。

次に、クラス オブジェクトの「Type」オブジェクトを取得し、Type オブジェクトの getProperties を使用してプロパティ情報からカスタム属性を取得します。クラス プロパティにはインターフェイス プロパティのカスタム属性がアタッチ/継承されていないため、クラス オブジェクトにカスタム属性は付与されません。

上記で取得したクラスの Type オブジェクトで GetInterface(NameOfImplementedInterfaceByclass) を呼び出します。これにより、インターフェイスの「Type」オブジェクトが提供されます。実装されたインターフェースの名前を知っている必要があります。Type オブジェクトからプロパティ情報を取得し、インターフェイスのプロパティにカスタム属性が添付されている場合、プロパティ情報はカスタム属性リストを提供します。実装クラスは、インターフェイスのプロパティの実装を提供している必要があります。インターフェイスのプロパティ情報のリスト内でクラス オブジェクトの特定のプロパティ名を照合して、カスタム属性リストを取得します。

これは機能します。

于 2019-04-30T13:49:11.407 に答える