这似乎意味着“不”。这是不幸的。
[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);
}
}
一个类可以从接口继承属性吗?或者我在这里咆哮错误的树?
答案 0 :(得分:64)
没有。每当在派生类中实现接口或覆盖成员时,都需要重新声明属性。
如果您只关心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
答案 1 :(得分:36)
您可以定义有用的扩展方法......
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>();
}
答案 2 :(得分:22)
Brad Wilson关于此的文章:Interface Attributes != Class Attributes
总结:类不从接口继承,它们实现它们。这意味着属性不会自动成为实现的一部分。
如果需要继承属性,请使用抽象基类,而不是接口。
答案 3 :(得分: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; }
}
然后在视图中:
@* 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)
答案 4 :(得分:3)
对于希望从已实现的接口上可能存在的属性中提取属性的人来说,这更多。因为这些属性不是类的一部分,所以这将允许您访问它们。请注意,我有一个简单的容器类,可以让您访问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; }
}
}
答案 5 :(得分:0)
编辑:这涵盖了从成员接口上的继承属性(包括属性)。对于类型定义,上面有简单的答案。我刚刚发布了此邮件,因为我发现它是一个令人讨厌的限制,想分享一个解决方案:)
接口是多重继承,在类型系统中表现为继承。这类事情没有充分的理由。反思有点曲折。我添加了评论来解释废话。
(这是.NET 3.5,因为它恰好是我目前正在使用的项目。)
// in later .NETs, you can cache reflection extensions using a static generic class and
// a ConcurrentDictionary. E.g.
//public static class Attributes<T> where T : Attribute
//{
// private static readonly ConcurrentDictionary<MemberInfo, IReadOnlyCollection<T>> _cache =
// new ConcurrentDictionary<MemberInfo, IReadOnlyCollection<T>>();
//
// public static IReadOnlyCollection<T> Get(MemberInfo member)
// {
// return _cache.GetOrAdd(member, GetImpl, Enumerable.Empty<T>().ToArray());
// }
// //GetImpl as per code below except that recursive steps re-enter via the cache
//}
public static List<T> GetAttributes<T>(this MemberInfo member) where T : Attribute
{
// determine whether to inherit based on the AttributeUsage
// you could add a bool parameter if you like but I think it defeats the purpose of the usage
var usage = typeof(T).GetCustomAttributes(typeof(AttributeUsageAttribute), true)
.Cast<AttributeUsageAttribute>()
.FirstOrDefault();
var inherit = usage != null && usage.Inherited;
return (
inherit
? GetAttributesRecurse<T>(member)
: member.GetCustomAttributes(typeof (T), false).Cast<T>()
)
.Distinct() // interfaces mean duplicates are a thing
// note: attribute equivalence needs to be overridden. The default is not great.
.ToList();
}
private static IEnumerable<T> GetAttributesRecurse<T>(MemberInfo member) where T : Attribute
{
// must use Attribute.GetCustomAttribute rather than MemberInfo.GetCustomAttribute as the latter
// won't retrieve inherited attributes from base *classes*
foreach (T attribute in Attribute.GetCustomAttributes(member, typeof (T), true))
yield return attribute;
// The most reliable target in the interface map is the property get method.
// If you have set-only properties, you'll need to handle that case. I generally just ignore that
// case because it doesn't make sense to me.
PropertyInfo property;
var target = (property = member as PropertyInfo) != null ? property.GetGetMethod() : member;
foreach (var @interface in member.DeclaringType.GetInterfaces())
{
// The interface map is two aligned arrays; TargetMethods and InterfaceMethods.
var map = member.DeclaringType.GetInterfaceMap(@interface);
var memberIndex = Array.IndexOf(map.TargetMethods, target); // see target above
if (memberIndex < 0) continue;
// To recurse, we still need to hit the property on the parent interface.
// Why don't we just use the get method from the start? Because GetCustomAttributes won't work.
var interfaceMethod = property != null
// name of property get method is get_<property name>
// so name of parent property is substring(4) of that - this is reliable IME
? @interface.GetProperty(map.InterfaceMethods[memberIndex].Name.Substring(4))
: (MemberInfo) map.InterfaceMethods[memberIndex];
// Continuation is the word to google if you don't understand this
foreach (var attribute in interfaceMethod.GetAttributes<T>())
yield return attribute;
}
}
准系统NUnit测试
[TestFixture]
public class GetAttributesTest
{
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
private sealed class A : Attribute
{
// default equality for Attributes is apparently semantic
public override bool Equals(object obj)
{
return ReferenceEquals(this, obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
private sealed class ANotInherited : Attribute { }
public interface Top
{
[A, ANotInherited]
void M();
[A, ANotInherited]
int P { get; }
}
public interface Middle : Top { }
private abstract class Base
{
[A, ANotInherited]
public abstract void M();
[A, ANotInherited]
public abstract int P { get; }
}
private class Bottom : Base, Middle
{
[A, ANotInherited]
public override void M()
{
throw new NotImplementedException();
}
[A, ANotInherited]
public override int P { get { return 42; } }
}
[Test]
public void GetsAllInheritedAttributesOnMethods()
{
var attributes = typeof (Bottom).GetMethod("M").GetAttributes<A>();
attributes.Should()
.HaveCount(3, "there are 3 inherited copies in the class heirarchy and A is inherited");
}
[Test]
public void DoesntGetNonInheritedAttributesOnMethods()
{
var attributes = typeof (Bottom).GetMethod("M").GetAttributes<ANotInherited>();
attributes.Should()
.HaveCount(1, "it shouldn't get copies of the attribute from base classes for a non-inherited attribute");
}
[Test]
public void GetsAllInheritedAttributesOnProperties()
{
var attributes = typeof(Bottom).GetProperty("P").GetAttributes<A>();
attributes.Should()
.HaveCount(3, "there are 3 inherited copies in the class heirarchy and A is inherited");
}
[Test]
public void DoesntGetNonInheritedAttributesOnProperties()
{
var attributes = typeof(Bottom).GetProperty("P").GetAttributes<ANotInherited>();
attributes.Should()
.HaveCount(1, "it shouldn't get copies of the attribute from base classes for a non-inherited attribute");
}
}
答案 6 :(得分:0)
添加具有属性的接口,该属性具有附加到类所具有的相同属性的属性/自定义属性。我们可以使用Visual Studio重构功能提取类的接口。 有一个局部类实现该接口。
现在获取类对象的“ Type”对象,并使用Type对象上的getProperties从属性信息中获取自定义属性。 这不会在类对象上提供自定义属性,因为类属性没有附加/继承接口属性的自定义属性。
现在,对上面检索到的类的Type对象调用GetInterface(NameOfImplemetedInterfaceByclass)。这将 提供接口的“类型”对象。我们应该知道实现的接口的名称。从Type对象获取属性信息,如果接口的属性附加了任何自定义属性,则属性信息将提供 自定义属性列表。实现类必须已提供接口属性的实现。 在接口的属性信息列表中匹配类对象的特定属性名称,以获取自定义属性列表。
这将起作用。
答案 7 :(得分:0)
尽管我的回答很晚并且针对特定情况,但我想补充一些想法。 正如其他答案中所建议的那样,反射或其他方法可以做到。
在我的情况下,所有模型都需要一个属性(时间戳)来满足实体框架核心项目中的某些要求(并发检查属性)。 我们可以在所有类属性的上方添加[](在实现模型的IModel接口中添加,但无效)。但是我通过Fluent API节省了时间,在这种情况下很有用。在fluent API中,我可以检查所有模型中的特定属性名称,并在1行中将其设置为IsConcurrencyToken()!
var props = from e in modelBuilder.Model.GetEntityTypes()
from p in e.GetProperties()
select p;
props.Where(p => p.PropertyInfo.Name == "ModifiedTime").ToList().ForEach(p => { p.IsConcurrencyToken = true; });
同样,如果您需要将任何属性添加到100多个类/模型的相同属性名称中,我们可以对内置或自定义属性解析器使用流利的api方法。 尽管EF(核心和EF6)流利的api可能会在后台使用反射,但我们可以节省精力:)