C#が汎用属性タイプを禁止しているのはなぜですか?
-
08-07-2019 - |
質問
これにより、コンパイル時の例外が発生します。
public sealed class ValidatesAttribute<T> : Attribute
{
}
[Validates<string>]
public static class StringValidation
{
}
C#は汎用属性をサポートしていないことを認識しています。ただし、多くのグーグル検索を行った後、その理由を見つけることができません。
ジェネリック型がAttribute
から派生できない理由を誰もが知っていますか?理論はありますか
解決
まあ、なぜ利用できないのかは答えられませんが、CLIの問題ではないことを 確認できます。 CLIの仕様では言及されておらず(私が見る限り)、ILを直接使用する場合は、汎用属性を作成できます。 C#3仕様の禁止部分-セクション10.1.4 <!> quot;クラスの基本仕様<!> quot;正当化するものではありません。
注釈付きのECMA C#2仕様は、有用な情報を提供していませんが、許可されていないものの例を提供しています。
注釈付きのC#3仕様のコピーが明日届くはずです。それがさらに情報を提供するかどうかを確認します。とにかく、それは間違いなくランタイムの決定ではなく言語の決定です。
編集:Eric Lippertからの回答(言い換え):あまり価値をもたらさないユースケースのために、言語とコンパイラの両方の複雑さを避けることを除いて、特別な理由はありません。
他のヒント
属性はコンパイル時にクラスを修飾しますが、ジェネリッククラスは実行時まで最終的な型情報を受け取りません。属性はコンパイルに影響を与える可能性があるため、<!> quot; complete <!> quot;でなければなりません。コンパイル時に。
詳細については、 MSDNの記事をご覧ください。
なぜ許可されないのかわかりませんが、これは回避策の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
{
....
}
これは真にジェネリックではなく、タイプごとに特定の属性クラスを記述する必要がありますが、ジェネリックベースインターフェースを使用して、少し防御的にコーディングし、必要とされるよりも少ないコードを記述し、ポリモーフィズムなどの利点を得ることができます。
//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<string>)
、typeof(Validates<SomeCustomType>)
など...
私の意見では、タイプに応じてカスタム検証が必要な場合、属性は最良のアプローチではないかもしれません。
おそらく、パラメータとしてSomeCustomValidationDelegate
またはISomeCustomValidator
を受け取る検証クラスの方が良いアプローチです。
私の回避策は次のようなものです:
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#言語について多くの議論がありますリポジトリ。
これは原則としては機能しますが、ほとんどにバグがあります 正しく動作しないようにするためのランタイムのバージョン 行使されなかった)。
どのターゲットランタイムが動作するかを理解するメカニズムが必要です。我々 多くのことのためにそれを必要とし、現在それを見ています。まで その後、それを取ることはできません。
十分な数を作成できる場合、メジャーC#バージョンの候補 ランタイムバージョンの多くがそれを処理します。