web-dev-qa-db-ja.com

C#が汎用属性タイプを禁止しているのはなぜですか?

これにより、コンパイル時例外が発生します。

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

C#はジェネリック属性をサポートしていません。しかし、多くのグーグル検索の後、私はその理由を見つけることができないようです。

ジェネリック型がAttributeから派生できない理由を誰もが知っていますか?理論はありますか?

483
Bryan Watts

まあ、なぜそれが利用できないのか答えられませんが、私はcanそれがCLIの問題ではないことを確認します。 CLIの仕様では言及されておらず(私が見る限り)、ILを直接使用する場合は、汎用属性を作成できます。 C#3仕様のそれを禁止する部分-セクション10.1.4「クラスベースの仕様」では、正当性が示されていません。

注釈付きのECMA C#2仕様も有用な情報を提供しませんが、許可されないものの例を提供します。

注釈付きのC#3仕様のコピーが明日届くはずです。それがさらに情報を提供するかどうかを確認します。とにかく、それは間違いなくランタイムの決定ではなく言語の決定です。

編集:Eric Lippertからの回答(言い換え):特別な理由はありません。ただし、言語とコンパイラーの両方で、あまり価値をもたらさないユースケースの複雑さを避けるためです。

340
Jon Skeet

属性はコンパイル時にクラスを修飾しますが、ジェネリッククラスは実行時まで最終的な型情報を受け取りません。属性はコンパイルに影響を与える可能性があるため、コンパイル時に「完全」でなければなりません。

詳細については、 MSDNの記事 を参照してください。

80
GalacticCowboy

許可されない理由はわかりませんが、これは回避策の1つです

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

これは真に汎用的なものではなく、タイプごとに特定の属性クラスを記述する必要がありますが、汎用ベースインターフェイスを使用して、少し防御的にコーディングし、必要なコードよりも少ないコードを記述し、ポリモーフィズムなどのメリットを得ることができます。

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

}
12
nawfal

これは非常に良い質問です。属性の私の経験では、属性を反映するときに、可能なすべての型の並べ替えを確認する必要がある条件を作成するため、制約が設定されていると思います:typeof(Validates<string>)typeof(Validates<SomeCustomType>)など...

私の意見では、タイプに応じてカスタム検証が必要な場合、属性は最良のアプローチではないかもしれません。

おそらく、SomeCustomValidationDelegateまたはISomeCustomValidatorをパラメーターとして使用する検証クラスの方が適切です。

8
ichiban

現在、これはC#言語の機能ではありませんが、 公式のC#言語レポについては多くの議論があります

会議メモ から:

これは原則としては機能しますが、ほとんどのバージョンのランタイムにはバグがあるため、正しく動作しません(実行されませんでした)。

どのターゲットランタイムが動作するかを理解するメカニズムが必要です。私たちは多くのことでそれを必要としており、現在それを見ています。それまで、私たちはそれを取ることができません。

十分な数のランタイムバージョンで処理できる場合は、メジャーC#バージョンの候補。

1
Owen Pauling

私の回避策は次のようなものです:

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