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

Why does C# forbid generic attribute types?

这将导致编译时异常:

1
2
3
4
5
6
7
8
9
10
public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

我意识到C不支持通用属性。然而,经过多次谷歌搜索,我似乎找不到原因。

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


嗯,我不能回答为什么它不可用,但我可以确认这不是一个CLI问题。cli规范没有提到它(据我所见),如果直接使用il,可以创建一个通用属性。C 3规范中禁止它的部分——第10.1.4节"类基础规范"没有给出任何理由。

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

我的标注C 3规格的副本明天就要到了…我看看是否能提供更多的信息。无论如何,这绝对是一个语言决定,而不是运行时决定。

编辑:来自EricLippert的回答(意译):没有特别的原因,除了为了避免语言和编译器的复杂性,对于一个没有多大价值的用例。


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

有关详细信息,请参阅此msdn文章。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
{
    ....
}


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//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
{

}

这是一个很好的问题。根据我对属性的经验,我认为约束已经到位,因为当考虑属性时,它将创建一个条件,在该条件下,您必须检查所有可能的类型排列:typeof(Validates)typeof(Validates)等。

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

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


我的解决方法是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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语言报告的讨论。

从一些会议笔记中:

Even though this would work in principle, there are bugs in most
versions of the runtime so that it wouldn't work correctly (it was
never exercised).

We need a mechanism to understand which target runtime it works on. We
need that for many things, and are currently looking at that. Until
then, we can't take it.

Candidate for a major C# version, if we can make a sufficient number
of runtime versions deal with it.