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

Bry*_*tts 494 c# generics .net-attributes

这会导致编译时异常:

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}
Run Code Online (Sandbox Code Playgroud)

我意识到C#不支持通用属性.然而,经过大量的谷歌搜索,我似乎无法找到原因.

有谁知道为什么泛型类型无法衍生出来Attribute?任何理论?

Jon*_*eet 350

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

带注释的ECMA C#2规范也没有给出任何有用的信息,尽管它确实提供了一个不允许的例子.

我的带注释的C#3规范的副本应该明天到达......我会看看是否会提供更多信息.无论如何,它绝对是一种语言决策,而不是运行时决定.

编辑:Eric Lippert的回答(释义):没有特别的原因,除了避免语言和编译器的复杂性,用于不增加太多价值的用例.

  • "没有增加太多价值的用例"?这是一个主观意见,它可以为我提供很多价值! (246认同)
  • "除了避免语言和编译器的复杂性"......以及来自给予我们合作和逆转的人们...... (137认同)
  • 最让我困扰的是没有这个功能的东西,不能做[PropertyReference(x => x.SomeProperty)]之类的东西.相反,你需要魔术字符串和typeof(),我认为这很糟糕. (33认同)
  • 我只想添加@Timwi的辩护,这不是唯一的地方[他们正在讨论](https://www.google.co.uk/search?q="generic + attributes"+ c%23 ),对这个问题的13K意见意味着健康的兴趣水平.另外:感谢获得权威答案,Jon. (14认同)
  • @John:我认为你大大低估了设计,指定,实施和测试新语言功能的成本. (13认同)
  • @asbjornu:是的,虽然有一个`infoof`运算符比使用lambda表达式更好... (6认同)
  • 我很高兴听到通用属性是合法的IL.如果你能问团队,我将非常感激!我有机会在PDC问安德斯并完全忘了:-) (3认同)
  • 它对我也很有价值,因为我发现没有办法绕过这个限制. (3认同)
  • 在你想要像我现在这样的功能之前,避免语言和编译器中不必要的复杂性是可以理解的.:)在这一点上,我认为语言已经有了泛型和属性,所以为什么不把它们放在一起 - 也许夏天的实习生需要微软做点什么. (3认同)
  • 我问过......我们会看看他们是否回复. (2认同)
  • 只是想问一些关于属性的问题.太悲伤的通用属性不可用.更糟糕的是,没有特别的理由.:/ (2认同)
  • @乔恩,的确,这有点嘲讽。我忽略了最后包含标准表情符号。 (2认同)
  • @Jon:在某些情况下,语言禁止构造(例如使用`System.Enum`作为通用约束),在没有规则禁止它的情况下 - 默认情况下只需"工作".虽然设计师可以自由地做任何他们想做的事情,但只有在产生效益的情况下投入努力的理念才会表明设计师不应该花费精力去禁止这样的结构,除非这样做会带来一些好处.简单地让通用属性类型的行为会产生什么影响,但是如果C#没有打扰禁止它们,它们会"自然"出现? (2认同)
  • @ygormutti:C#6将具有`nameof` ...不像`infoof`那么强大,但是要简单得多. (2认同)

Gal*_*boy 83

属性在编译时修饰一个类,但泛型类在运行时之前不会收到它的最终类型信息.由于该属性可能会影响编译,因此必须在编译时"完成".

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

  • 这似乎与另一个答案相矛盾,该答案表明IL将允许它.(/sf/answers/20598161/) (4认同)
  • 文章重申,它们是不可能的,但没有理由.我从概念上理解你的答案.你知道关于这个问题的更多官方文件吗? (3认同)
  • 本文确实涵盖了IL仍然包含在运行时用实际类型替换的通用占位符这一事实.其余的由我推断...... :) (2认同)

Gee*_*key 20

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

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

  • 可悲的是,我不想这样做,这就是我发现这个问题的原因.我想我只需坚持处理typeof.现在仿制药存在了这么久之后,它真的感觉像是一个肮脏的关键词. (14认同)
  • 不幸的是,在使用属性时会丢失编译时输入.想象一下,该属性创建了泛型类型的东西.你可以解决它,但它会很好; 这是你不能做的那些直观的事情之一,比如方差(目前). (3认同)

Hos*_*imi 16

自 C# 11 起通用属性可用。现在,这是可能的:

[GenericAttribute<int>()]
public int Method();
Run Code Online (Sandbox Code Playgroud)

然而,这还不可能:

[GenericAttribute<T>()]
public int Method<T>(T param);
Run Code Online (Sandbox Code Playgroud)

T在编译时是未知的。

还,

类型参数必须满足与 typeof 运算符相同的限制。不允许需要元数据注释的类型。例如,不允许使用以下类型作为类型参数:

  • dynamic
  • string?(或任何可为空的引用类型)
  • (int X, int Y)(或使用 C# 元组语法的任何其他元组类型)。

这些类型不直接在元数据中表示。它们包括描述类型的注释。在所有情况下,您都可以使用基础类型:

  • object为了dynamic
  • string代替string?
  • ValueTuple<int, int>代替(int X, int Y)

来源:https ://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-attributes

  • 这非常棒,但它似乎不允许参数类型 `Expression&lt;Func&lt;T, object&gt;&gt;` (可能在这里做错了什么),这意味着不可能执行类似以下操作:`[Lookup&lt;Child&gt; (c =&gt; c.ParentId)]` (2认同)

naw*_*fal 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
{

}
Run Code Online (Sandbox Code Playgroud)


ich*_*ban 8

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

在我看来,如果根据类型需要自定义验证,属性可能不是最好的方法.

接受a SomeCustomValidationDelegateISomeCustomValidator作为参数的验证类可能是更好的方法.

  • 你可以检查泛型类型定义(即typeof(Validates <>))... (4认同)

Owe*_*ing 5

这目前不是 C# 语言功能,但是在官方 C# 语言 repo 上有很多讨论

来自一些会议笔记

尽管这在原则上是可行的,但大多数版本的运行时都存在错误,因此无法正常工作(从未执行过)。

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

主要 C# 版本的候选者,如果我们可以让足够数量的运行时版本处理它。