为什么c#禁止泛型属性类型?

这会导致编译时异常:

public sealed class ValidatesAttribute<T> : Attribute
{


}


[Validates<string>]
public static class StringValidation
{


}

我意识到c#不支持泛型属性。然而,在谷歌搜索了很多次之后,我似乎找不到原因。

有人知道为什么泛型类型不能从Attribute派生吗?什么理论吗?

82554 次浏览

属性在编译时修饰类,但泛型类直到运行时才接收其最终类型信息。因为属性会影响编译,所以它必须在编译时是“完整的”。

更多信息请参见MSDN文章

这是一个非常好的问题。根据我使用属性的经验,我认为这个约束是合适的,因为当反思一个属性时,它会创建一个条件,在这种条件下,您必须检查所有可能的类型排列:typeof(Validates<string>)typeof(Validates<SomeCustomType>),等等……

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

也许采用SomeCustomValidationDelegateISomeCustomValidator作为参数的验证类是更好的方法。

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

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

我的带注释的c# 3规范应该明天就能到…我看看能不能提供更多信息。无论如何,这绝对是一个语言决定,而不是运行时决定。

编辑:Eric Lippert的回答(转述):没有特别的原因,只是为了避免语言和编译器的复杂性,这并没有增加多少价值。

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

[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
{
....
}

这并不是真正的泛型,您仍然需要为每个类型编写特定的属性类,但是您可以使用泛型基本接口来编写一些防御性的代码,编写比其他需要的代码更少的代码,获得多态性的好处等。

//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
{


}

我的变通方法是这样的:

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

这目前还不是c#语言的特性,但是有很多关于官方c#语言回购的讨论

从# EYZ0:

尽管这在原则上是可行的,但大多数都存在bug 运行时的版本,使它不能正常工作(它是 从来没有行使)。< / p > 我们需要一种机制来理解它在哪个目标运行时上工作。我们 很多事情都需要它,目前正在考虑。直到

主要c#版本的候选,如果我们能做出足够的数量

通用属性已经作为预览特性添加到c# 11中,这意味着你必须设置& lt; LangVersion>预览来启用这个特性;在最终发布之前,该功能可能会有所改变。

不过该功能也有一些限制:

应用属性时,必须提供所有类型参数。换句话说,泛型类型必须完全构造。

public class GenericType<T>
{
[GenericAttribute<T>()] // Not allowed! generic attributes must be fully closed types.
public string Method() => default;
}

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

  • # EYZ0
  • string?(或任何可空引用类型)
  • (int X, int Y)(或使用c#元组语法的任何其他元组类型)。
这些类型不直接在元数据中表示。它们包括以下注释 描述类型。在所有情况下,都可以使用底层类型 而不是:< / p >
  • objectdynamic
  • string而不是string?
  • ValueTuple<int, int>而不是(int X, int Y)

来源:# EYZ0