为什么C#禁止使用通用属性类型?


510

这会导致编译时异常:

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

我意识到C#不支持通用属性。但是,经过大量谷歌搜索之后,我似乎找不到原因。

有谁知道为什么泛型类型不能衍生自Attribute?有什么理论吗?


17
你可以做[Validates(typeof(string)]-我同意泛型会更好...
ConsultUtah 2010年

20
即使这是该问题的最新补充,但令人遗憾的是,不仅不分配属性本身,而且也不允许抽象属性类(显然无论如何都不能将其实例化为属性),就像这样:abstract class Base<T>: Attribute {}可用于创建非通用派生类如下:class Concrete: Base<MyType> {}
Lucero 2010年

88
我渴望通用属性和接受lambda的属性。想象一下诸如此类的事物[DependsOnProperty<Foo>(f => f.Bar)][ForeignKey<Foo>(f => f.IdBar)]...
JacekGorgoń2011年

3
在我刚遇到的情况下,这将非常有用;最好创建一个接受泛型类型并在指定的实际值上强制使用该类型的LinkedValueAttribute。我可以将它用于枚举,以指定另一个枚举的“默认”值(如果选择了该枚举值)。可以为不同的类型指定多个这些属性,并且我可以根据所需的类型获得所需的值。我可以将其设置为使用“类型”和“对象”,但是强类型化将是一个巨大的优势。
KeithS 2012年

10
如果您不介意IL,这看起来很有希望
Jarrod Dixon

Answers:


358

好吧,我无法回答为什么它不可用,但是我可以确认这不是CLI问题。CLI规范没有提到它(据我所知),如果直接使用IL,则可以创建一个通用属性。C#3规范禁止该部分的部分-第10.1.4节“类基本规范”没有给出任何理由。

带注释的ECMA C#2规范也没有提供任何有用的信息,尽管它提供了不允许使用的示例。

带注释的C#3规范的副本应于明天到达...我将看看是否能提供更多信息。无论如何,这绝对是语言决定,而不是运行时决定。

编辑:埃里克·利珀特(Eric Lippert)的回答(释义):没有特殊的原因,除了在用例中避免语言和编译器的复杂性之外,这种用例不会增加太多价值。


139
“除了避免在语言和编译器上都
变得

254
“用例并没有增加太多价值”?这是一个主观意见,可以为我提供很多价值!
乔恩·克鲁格

34
最令我困扰的是没有此功能,因为它无法执行[PropertyReference(x => x.SomeProperty)]之类的事情。相反,您需要魔术字符串和typeof(),我认为这很糟糕。
阿斯比约恩Ulsberg

13
@John:我认为您大大低估了设计,指定,实施和测试新语言功能的成本。
乔恩·斯基特

14
我只想补充一下@Timwi的辩护,即这并不是他们正在讨论的唯一地方,并且在此问题上的13000观点暗示着人们的关注度很高。另外:乔恩,感谢您获得权威的答复。
约旦·格雷

84

属性在编译时修饰类,但泛型类直到运行时才接收其最终类型信息。由于该属性会影响编译,因此在编译时必须“完整”。

有关更多信息,请参见此MSDN文章


3
文章重申它们是不可能的,但是没有理由。我从概念上理解您的答案。您是否知道有关此问题的更多官方文档?
布赖恩·瓦茨

2
本文确实涵盖了以下事实:IL仍包含在运行时用实际类型替换的通用占位符。其余的由我推断... :)
GalacticCowboy

1
就其价值而言,VB实施了相同的约束:“泛型或包含在泛型类型中的类不能从属性类继承。”
GalacticCowboy

1
ECMA-334,第14.16节说:“在下面列出的上下文中需要使用常量表达式,并且在语法中使用常量表达式来表示。在这些上下文中,如果无法在编译时完全评估表达式,则会发生编译时错误。时间。” 属性在列表中。
GalacticCowboy

4
这似乎与另一个回答矛盾,该回答指出IL将允许它。(stackoverflow.com/a/294259/3195477
UuDdLrLrSs

21

我不知道为什么不允许这样做,但这是一种可能的解决方法

[AttributeUsage(AttributeTargets.Class)]
public class ClassDescriptionAttribute : Attribute
{
    public ClassDescriptionAttribute(Type KeyDataType)
    {
        _KeyDataType = KeyDataType;
    }

    public Type KeyDataType
    {
        get { return _KeyDataType; }
    }
    private Type _KeyDataType;
}


[ClassDescriptionAttribute(typeof(string))]
class Program
{
    ....
}

3
不幸的是,使用该属性时会丢失编译时的输入。想象一下,该属性创建了某种通用类型。您可以解决它,但这会很好。这是您无法做的那些直观的事情之一,例如方差(当前)。
布赖恩·瓦茨

14
可悲的是尝试不这样做,这就是为什么我发现了这个问题。我想我只需要坚持处理typeof。在泛型已经存在了很长时间之后,现在确实感觉像个肮脏的关键字。
克里斯·马里西克

13

这并不是真正的通用,并且您仍然必须为每种类型编写特定的属性类,但是您可以使用通用的基本接口进行防御性编码,编写比其他要求少的代码,获得多态性的好处等。

//an interface which means it can't have its own implementation. 
//You might need to use extension methods on this interface for that.
public interface ValidatesAttribute<T>
{
    T Value { get; } //or whatever that is
    bool IsValid { get; } //etc
}

public class ValidatesStringAttribute : Attribute, ValidatesAttribute<string>
{
    //...
}
public class ValidatesIntAttribute : Attribute, ValidatesAttribute<int>
{
    //...
}

[ValidatesString]
public static class StringValidation
{

}
[ValidatesInt]
public static class IntValidation
{

}

8

这个问题问得好。在我与属性的经验,我认为约束是在地方,因为一个属性反映时,将创建您必须检查所有可能的排列型的条件:typeof(Validates<string>)typeof(Validates<SomeCustomType>),等...

我认为,如果需要根据类型进行自定义验证,则属性可能不是最佳方法。

将a SomeCustomValidationDelegateISomeCustomValidatoras作为参数的验证类可能是一种更好的方法。


我同意你的看法。我这个问题已经存在很长时间了,目前正在构建一个验证系统。我使用当前的术语来提出问题,但无意实施基于此机制的方法。
布赖恩·瓦茨

在为相同目标进行设计时,我偶然发现了这一点:验证。我正在尝试以一种易于自动分析(即,您可以生成描述应用程序中验证的报告以进行确认)和人工可视化代码的方式来进行操作。如果不是属性,我不确定最好的解决方案是什么...我仍然可以尝试属性设计,但是手动声明特定于类型的属性。这还需要做更多的工作,但是目的是为了确保知道验证规则(并能够报告这些规则以进行确认)的可靠性。
bambams 2010年

4
您可以检查泛型类型定义(即typeof(Validates <>))...
Melvyn

5

目前,这不是C#语言功能,但是有关官方C#语言存储库的讨论很多

一些会议记录中

即使从原理上讲这是可行的,但是在大多数版本的运行时中都存在错误,因此它无法正确运行(从未使用过)。

我们需要一种机制来了解它在哪个目标运行时上运行。我们在很多方面都需要它,目前正在研究中。在那之前,我们不能接受。

如果可以使足够多的运行时版本处理它,则可以是主要C#版本的候选者。


1

我的解决方法是这样的:

public class DistinctType1IdValidation : ValidationAttribute
{
    private readonly DistinctValidator<Type1> validator;

    public DistinctIdValidation()
    {
        validator = new DistinctValidator<Type1>(x=>x.Id);
    }

    public override bool IsValid(object value)
    {
        return validator.IsValid(value);
    }
}

public class DistinctType2NameValidation : ValidationAttribute
{
    private readonly DistinctValidator<Type2> validator;

    public DistinctType2NameValidation()
    {
        validator = new DistinctValidator<Type2>(x=>x.Name);
    }

    public override bool IsValid(object value)
    {
        return validator.IsValid(value);
    }
}

...
[DataMember, DistinctType1IdValidation ]
public Type1[] Items { get; set; }

[DataMember, DistinctType2NameValidation ]
public Type2[] Items { get; set; }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.