次の汎用クラスについて考えてみます。
public class Custom<T> where T : string
{
}
これにより、次のエラーが発生します。
'string'は有効な制約ではありません。制約として使用される型は、インターフェース、非シールクラス、または型パラメーターである必要があります。
ジェネリッククラスが使用できるタイプを制限する別の方法はありますか?
また、複数のタイプに制限できますか?
例えば
Tは、string、int、またはbyteのみです。
次の汎用クラスについて考えてみます。
public class Custom<T> where T : string
{
}
これにより、次のエラーが発生します。
'string'は有効な制約ではありません。制約として使用される型は、インターフェース、非シールクラス、または型パラメーターである必要があります。
ジェネリッククラスが使用できるタイプを制限する別の方法はありますか?
また、複数のタイプに制限できますか?
例えば
Tは、string、int、またはbyteのみです。
public class Custom<T> where T : string
それを満たすのは: ( is )-ジェネリックとしてはかなり無意味であるため、許可されません。Tstringstringsealed
また、複数のタイプに制限できますか?
いいえ-制約ではなくリフレクションを介して実行時にそれを行わない限り(静的コンストラクターはそれを行う1つの方法です-誤って使用された場合は例外をスローします)
T can only be string, int or byte
のようなものを使用するかもしれIEquatable<T>ませんが、それはあなたが望むほどそれを制限しないので、最終的には:いいえ。
あなたができることは、過負荷のファクトリを介してそれにアクセスすることです:
public abstract class Custom
{
public static Custom Create(int value)
{ return new CustomImpl<int>(value); }
public static Custom Create(byte value)
{ return new CustomImpl<byte>(value); }
public static Custom Create(string value)
{ return new CustomImpl<string>(value); }
private class CustomImpl<T> : Custom
{
public CustomImpl(T val) { /*...*/ }
}
}
私の経験から、なぜあなたが欲しいのか理解していると思います、stringそして...文字列型または整数型のIDintを持つ汎用基本クラスのため
しかし、これが不可能であることは確かです。このmsdnの説明にあるように:http: //msdn.microsoft.com/en-us/library/d5x73970%28v=vs.80%29.aspx
制約class(stringのような参照オブジェクト)またはstruct(intのようなValueType)を持つことができるので、stringとintを混在させることはできません
注:文字列は封印されているため、文字列のエラーは理にかなっています。したがって、一般的なものである必要はありません。文字列IDが必要です。
ここで回答を確認し、少し遊んだ後、次の実装を思いつきました。これは、コンパイル時ではなく実行時に制約をチェックします。
// This example takes 3 parameters...
public class GenericConstraint<T1, T2, T3>
{
public GenericConstraint(Type type)
{
if (!(type is T1) || !(type is T2) || !(type is T3))
{
throw new Exception("This is not a supported type");
}
}
}
これをカスタムクラスから継承します...
public class Custom<T> : GenericConstraint<string, int, byte>
{
public Custom() : base(typeof(T))
{
}
}
これでエラーがスローされます。
Custom<long> item = new Custom<long>();
これはしません!
Custom<byte> item2 = new Custom<byte>();
Marc Gravellが述べたように、これは継承やジェネリックの適切な使用法ではありません。これを論理的に考えると、GenericConstraintを継承することで、継承がこれだけに制限され、型階層が適切に使用されなくなります。ジェネリックの使用に関しては、これは実際にはかなり無意味です!
したがって、実行時に型を制約するヘルパーメソッドとして機能する別のソリューションがあります。これにより、オブジェクトが継承から解放されるため、型階層には影響しません。
public static void ConstrainParameterType(Type parameterType, GenericConstraint constraintType, params Type[] allowedTypes)
{
if (constraintType == GenericConstraint.ExactType)
{
if (!allowedTypes.Contains<Type>(parameterType))
{
throw new Exception("A runtime constraint disallows use of type " + parameterType.Name + " with this parameter.");
}
}
else
{
foreach (Type constraint in allowedTypes)
{
if (!constraint.IsAssignableFrom(parameterType))
{
throw new Exception("A runtime constraint disallows use of type " + parameterType.Name + " with this parameter.");
}
}
}
}
public enum GenericConstraint
{
/// <summary>
/// The type must be exact.
/// </summary>
ExactType,
/// <summary>
/// The type must be assignable.
/// </summary>
AssignableType
}
これにより、型がシールされている場合などでも、ジェネリックオブジェクトに複数の型制約を適用できるようになりました。
「publicclassCustomwhere T:string ...は許可されていません。これは、それに一致するTがstring(文字列は封印されている)のみであるためです。これにより、ジェネリックとしては無意味になります。」
はい、これは無意味ですが、状況によっては、たとえば、許可するようにオブジェクトを制約したい場合があります。String、StringBuilder、SecureString。これはコンパイル時の制約を提供しませんが、実行時の制約を提供し、制約で使用できる型にある程度の柔軟性を提供します。