问题 限制自定义属性,以便它只能应用于C#中的特定类型?


我有一个自定义属性,它应用于类属性和类本身。现在,必须应用我的自定义属性的所有类都派生自单个基类。

如何限制我的自定义属性,以便它只能应用于那些必须从我的基类派生的类?我该怎么做呢?


5685
2018-06-20 07:57


起源

你想强制它(以便它自动完成)或限制它(所以它不能应用于其他类型)? - Fredrik Mörk
我想限制它,以便它不能应用于那些不是从我的基类派生的类型。 - this. __curious_geek
编辑:darn,我认为通过找到一种方法来解决自己的问题!给我一个新的...... - Marc Gravell♦


答案:


Darn,当我证明自己错了时,我讨厌它...如果你将属性定义为a,它就可以了 保护 基类的嵌套类型:

abstract class MyBase {
    [AttributeUsage(AttributeTargets.Property)]
    protected sealed class SpecialAttribute : Attribute {}
}

class ShouldBeValid : MyBase {
    [Special]
    public int Foo { get; set; }
}
class ShouldBeInvalid {
    [Special] // type or namespace not found
    [MyBase.Special] // inaccessible due to protection level
    public int Bar{ get; set; }
}

(原始答案)

你不能这样做 - 至少,不是在编译时。

属性可以被限制(例如)“class”,“struct”,“method”,“field”等等 - 但不能更精细。

当然,由于属性本身不做任何事情(忽略PostSharp),它们对其他类型是惰性的(并且对你的类型不敏感,除非你在运行时添加一些代码来查看属性)。

你可以写一个FxCop规则,但这似乎有点过分。

不过,我想知道属性是否是最佳选择:

现在,必须应用我的自定义属性的所有类都派生自单个基类。

如果他们 必须 应用您的自定义属性,也许是 abstract (或者只是 virtual)属性/方法会是更好的选择吗?

abstract class MyBase {
    protected abstract string GetSomeMagicValue {get;}
}
class MyActualType : MyBase {
    protected override string GetSomeMagicValue {get {return "Foo";}}
}

15
2018-06-20 08:28



谢谢马克。我正在将属性应用于属性以进行映射。基类有一个方法返回派生类中应用了此属性的所有属性。此外,没有任何意义允许将属性应用于任何类型或成员而没有重要目的。 - this. __curious_geek
即使我没有找到任何这样做的文档,所以在我得出任何结论之前我想给它一个stackoverflow的机会。我相信属性是有目的地应用的,所以为什么允许它应用于那些不具备目的的类型或成员。 - this. __curious_geek
“那么为什么要允许它” - 简单地说,你现在无法阻止它。没有机制可以禁止它。 - Marc Gravell♦
我发现这个有趣的博客有趣: marcgravell.blogspot.com/2009/06/... - Marc Gravell♦
@Marc:谢谢marc,你摇滚。我在设计和实现自己的紧凑型ORM时遇到了这种需求。我几乎完成了我的工作,并期待将属性用法限制为仅从我的EntityBase类派生的类。 - this. __curious_geek


我不知道有任何方法可以使用自定义属性。
更好的方法是让类实现具有泛型约束的接口

所以

class MyAttribute
{
     // put whatever custom data you want in here
}

interface IAttributeService<T> where T : MyBaseClass
{
     MyAttribute Value{get;}
}

然后你的基类会这样做

class MyBaseClass : IAttributeService<MyBaseClass>
{
     private MyAttribute m_attrib;

     IAttributeService<MyClass>.Value
     {
         get {return m_attrib;}
     }
}

这样编译器将在编译时强制执行约束,以便只有类
派生自MyBaseClass可以实现该接口。
我通过定义一个MyAttribute类使其相当可扩展,但是如果你需要更简单的东西,你当然可以返回一个字符串或一个bool

我还将MyAttribute成员设为私有,以便派生类无法看到它并更改它但可以使其受到保护并允许派生类访问(如果您愿意)。


0
2018-06-20 09:03