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

时间:2008-11-16 18:48:03

标签: c# generics .net-attributes

这会导致编译时异常:

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

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

有谁知道为什么泛型类型无法从Attribute派生?任何理论?

7 个答案:

答案 0 :(得分:337)

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

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

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

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

答案 1 :(得分:80)

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

有关详细信息,请参阅此MSDN article

答案 2 :(得分:18)

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

[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 :(得分:12)

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

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

}

答案 4 :(得分:8)

这是一个非常好的问题。根据我对属性的体验,我认为约束已经到位,因为在反映属性时,它会创建一个条件,您必须检查所有可能的类型排列:typeof(Validates<string>)typeof(Validates<SomeCustomType>)等...

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

或许以SomeCustomValidationDelegateISomeCustomValidator作为参数的验证类可能是更好的方法。

答案 5 :(得分: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; }

答案 6 :(得分:1)

这当前不是C#语言功能,但是there is much discussion on the official C# language repo

来自some meeting notes

  

即使原则上可行,但大多数错误   版本的运行时,使其无法正常工作(   从未锻炼过。)

     

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

     

主要C#版本的候选人(如果我们可以提供足够数量的候选人)   的运行时版本对此进行了处理。