C#にnew()制約があるのに、他の同様の制約がないのはなぜですか?


19

C#ジェネリックでは、と言うことで、型パラメーターの制約を宣言Tしてデフォルトコンストラクターを持たせることができますwhere T : new()。ただし、このような他の種類の制約は有効ではありません- new(string)たとえば、など

言語設計および/または実装の観点から、この理由は何ですか?

コンストラクタの動作方法や、これを禁止する(または少なくとも難しくする)型システムの実装方法に何かありますか?もしそうなら、それは何ですか?default(T)実際にコンパイルさnew T()れた場所を読んだことを思い出しT : structます。これと関係があるのでしょうか?

それとも、言語が複雑になりすぎないようにするために、単に設計上の決定を下しただけですか?


new(string)デフォルトのコンストラクター制約ではありません。あなたの質問は、「特定のコンストラクター署名を必要とする制約がないのはなぜか」ということと同じです。そのような制約は特に有用ではないからです。
ロバートハーヴェイ14

3
@RobertHarveyはい、それはまさに私が言っていることです。基本的に、デフォルトのコンストラクタを特別な実装にする特別なものがあるのか​​、それとも他のものではなくこれを含めるだけの任意の選択肢であるのかを尋ねています。
セオドロスチャツィジアンナキス14

デフォルトのコンストラクターは、特定の重要な方法で役立ちます。たとえば、型を簡単にシリアル化できるようにします。
ロバートハーヴェイ14

6
特定のctor署名が役立つ場合があります。たとえば、型変数がT(Collection <T>)のアクタを持つCollection <T>に制限されている場合、別のコレクションを指定して新しいコレクションを構築できることがわかります。しかし、その有用性が余分な複雑さに見合うかどうかは疑問です。
Phoshi

回答:


5

信頼できる回答については、数年前のStackOverflowに関する質問に対するEric Lippertの回答を参照してください。その抜粋を以下に簡単に引用します。

ただし、この特定のケースでは、デザインミーティングで将来のバージョンの言語の可能な機能として登場した機能を押し戻す理由を確かに説明できます。

...

機能全体を実行するか、まったく実行しないかのどちらかです。特定のコンストラクターを持つように型を制限できることが重要な場合は、機能全体を実行し、コンストラクターだけでなく一般的なメンバーに基づいて型を制限しましょう。


2
文脈から取られたその引用は、非常に誤解を招くものです。それは、エリックがマイクロソフトが「ずっと行き過ぎている」べきだと考えたことを示唆しているが、それは彼の立場ではない。実際、彼は提案された機能に正反対です。
ロバートハーヴェイ14

1
おかげで、これは私の質問に部分的に答えています。彼の答えの終わり近くで、Eric LippertはそれがILの制限であり、この機能を含めるにはILへの追加が必要だと述べています。あなたはILの一部のために生成されたもので、ソースを提供することができればこれは完璧になるだろうされ、一般的にデフォルトコンストラクタを呼び出し、ある-実装を。
セオドロスチャツィジアンナキス14

@TheodorosChatzigiannakis:TelerikのDecompilerを起動して、自分で調べてみませんか?
ロバートハーヴェイ14

2
@RobertHarvey私は彼の立場をまったく示唆しているとは思わないが、彼の立場を強調するために追加の引用を含めた。
クリスハノン14

1
うーん、F#には、クラスに演算子がある場合にコンパイル時としてチェックするなど、型を制約するためのより高度な方法があります。おそらく、F#は非常に厳密な型チェックのため、C#よりも強力な制約システムを必要とします。それにもかかわらず、この言語は、.Net Frameworkでクラスを保持するための高度な方法を実装できます。
OnesimusUnbound 14

16

(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#にとって特別なものです。ジェネリックのより複雑な制約の有効なユースケースがいくつかあるとしても、他のコンストラクタに同じ処理を与えることはコストがかかるでしょう。


4
面白い。default(T) 値型の重複呼び出しが必要な理由
アヴナーシャハルカシュタン14

2
@ AvnerShahar-Kashtan知りません。t変数(ネストされたreturnステートメントで置き換えることができる)のような、コンパイル/逆コンパイルプロセスのアーティファクトである可能性があります。
テオドロスチャツィジアンナキス14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.