C#ジェネリックでは、where T : new()
と言うことで、型パラメーターT
の制約を宣言してデフォルトのコンストラクターを持つことができます。ただし、このような他の種類の制約は有効ではありません。たとえば、new(string)
などです。
言語設計および/または実装の観点から、これの理由は何ですか?
これを禁止する(または少なくとも難しくする)コンストラクタの動作方法または型システムの実装方法に何かありますか?もしそうなら、それは何ですか? default(T)
が実際にnew T()
にコンパイルされてT : struct
。これに関係があるのでしょうか?
それとも、言語が複雑になりすぎないようにするために、単に設計上の決定を行ったのでしょうか。
信頼できる回答については、数年前のStackOverflowでのその質問の Eric Lippertの回答 を参照してください。その抜粋を以下に簡単に引用します。
ただし、この特定のケースでは、今後のバージョンの言語で可能な機能として設計会議で機能が登場した場合に、その機能をプッシュバックする理由をいくつか説明できます。
...
機能全体を実行するか、まったく実行しないかのどちらかです。タイプを特定のコンストラクターを持つように制限できることが重要である場合は、機能全体を実行して、コンストラクターだけでなく、一般的なメンバーに基づいてタイプを制限しましょう。
(Robert Harveyの提案に従って)逆コンパイルすると、興味のある人は次のようになりました。この方法:
_static T GenericMake<T>()
where T : new()
{
return new T();
}
_
どうやら、コンパイルすると、次のようになります:
_private static T GenericMake<T>()
where T : new()
{
T t;
T t1 = default(T);
if (t1 == null)
{
t = Activator.CreateInstance<T>();
}
else
{
t1 = default(T);
t = t1;
}
return t;
}
_
T
が値タイプの場合、new()
はdefault(T)
になります。T
が参照型の場合、new()
はリフレクションを使用して機能します。 Activator.CreateInstance()
は内部でRuntimeType.CreateInstanceDefaultCtor()
を呼び出します。つまり、内部的には、デフォルトのコンストラクターはCLRに関してC#にとって本当に特別なものです。ジェネリックスのより複雑な制約の有効な使用例がいくつかあるとしても、他のコンストラクターに同じ処理を与えるとコストがかかります。