290

.NET では、値型 (C# struct) にパラメーターのないコンストラクターを含めることはできません。この投稿によると、これは CLI 仕様によって義務付けられています。何が起こるかというと、すべての値型に対して、すべてのメンバーをゼロ (またはnull) に初期化するデフォルトのコンストラクターが (コンパイラーによって?) 作成されます。

そのようなデフォルトのコンストラクターを定義することが許可されていないのはなぜですか?

1 つの些細な使用法は、有理数です。

public struct Rational {
    private long numerator;
    private long denominator;

    public Rational(long num, long denom)
    { /* Todo: Find GCD etc. */ }

    public Rational(long num)
    {
        numerator = num;
        denominator = 1;
    }

    public Rational() // This is not allowed
    {
        numerator = 0;
        denominator = 1;
    }
}

C# の現在のバージョンを使用すると、デフォルトの Rational は0/0それほどクールではありません。

PS : C# 4.0 でこれを解決するには、既定のパラメーターを使用しますか? それとも、CLR で定義された既定のコンストラクターが呼び出されますか?


ジョン・スキートはこう答えました。

あなたの例を使用すると、誰かがしたときに何が起こりたいですか:

 Rational[] fractions = new Rational[1000];

コンストラクターを 1000 回実行する必要がありますか?

確かにそうすべきです。それが、最初にデフォルトのコンストラクターを作成した理由です。明示的な既定のコンストラクターが定義されていない場合、CLR は既定のゼロ化コンストラクターを使用する必要があります。そうすれば、使用した分だけ支払うことができます。次に、デフォルト以外の 1000 個のコンテナーが必要な場合Rational(および 1000 個の構造を最適化したい場合)List<Rational>は、配列ではなく a を使用します。

私の考えでは、この理由は、デフォルトのコンストラクターの定義を妨げるほど強力ではありません。

4

12 に答える 12

211

注:以下の回答は、構造体でパラメーターなしのコンストラクターを宣言する機能を導入することを計画している C# 6 よりもずっと前に書かれていますが、すべての状況 (配列の作成など) で呼び出されるわけではありません (最終的にはこの機能は C# 6 には追加されていません)。


編集:グラウエンウルフのCLRへの洞察により、以下の回答を編集しました。

CLR では値型にパラメーターなしのコンストラクターを使用できますが、C# では使用できません。これは、コンストラクターが呼び出されないときに呼び出されるという期待が導入されるためだと思います。たとえば、次のように考えてください。

MyStruct[] foo = new MyStruct[1000];

CLR は、適切なメモリを割り当ててすべてゼロにするだけで、これを非常に効率的に行うことができます。MyStruct コンストラクターを 1000 回実行する必要がある場合、効率は大幅に低下します。(実際にはそうではありませんパラメーターなしのコンストラクターがある場合、配列を作成するとき、または初期化されていないインスタンス変数があるときは実行されません。)

C# の基本的なルールは、「どの型の既定値も初期化に依存できない」です。今では、パラメーターなしのコンストラクターを定義できるようにすることもできましたが、そのコンストラクターをすべての場合に実行する必要はありませんでした。(または、少なくとも、私は議論がうまくいくと信じています。)

編集:あなたの例を使用するには、誰かがしたときにどうしたいですか:

Rational[] fractions = new Rational[1000];

コンストラクターを 1000 回実行する必要がありますか?

  • そうでない場合、1000 の無効な根拠が得られます。
  • その場合、配列に実際の値を入力しようとすると、大量の作業が無駄になる可能性があります。

編集:(質問にもう少し答える)パラメーターなしのコンストラクターは、コンパイラーによって作成されません。CLR に関する限り、値型にコンストラクターを含める必要はありませんが、IL で記述した場合はコンストラクターを使用できることがわかります。C# で" " を記述new Guid()すると、通常のコンストラクターを呼び出した場合とは異なる IL が出力されます。その側面についてもう少し詳しくは、この SO の質問を参照してください。

パラメーターなしのコンストラクターを使用するフレームワークには、値の型がないと思われます。NDepend は、私が適切に質問すれば間違いなく教えてくれます... C# がそれを禁止しているという事実は、おそらく悪い考えだと考えるのに十分なヒントです。

于 2008-12-02T12:48:21.920 に答える
54

構造体は値型であり、値型は宣言されるとすぐにデフォルト値を持つ必要があります。

MyClass m;
MyStruct m2;

どちらもインスタンス化せずに上記のように 2 つのフィールドを宣言すると、デバッガーが中断され、 mnull になりますが、nullにm2はなりません。これを考えると、パラメーターのないコンストラクターは意味がありません。実際、構造体のコンストラクターは値を割り当てるだけで、宣言するだけで物自体がすでに存在します。実際、m2 は上記の例で非常にうまく使用でき、そのメソッドがあれば呼び出され、そのフィールドとプロパティが操作されます!

于 2008-12-02T16:58:28.093 に答える
14

簡単な説明:

C ++では、構造体とクラスは同じコインの表裏にすぎませんでした。唯一の本当の違いは、一方がデフォルトでパブリックで、もう一方がプライベートであったことです。

.NETでは、構造体とクラスの間にはるかに大きな違いがあります。主なことは、structが値型のセマンティクスを提供し、classが参照型のセマンティクスを提供することです。この変更の影響について考え始めると、説明するコンストラクターの動作など、他の変更もより意味をなすようになります。

于 2008-12-02T14:03:56.293 に答える
2

特殊なケースです。分子が 0 で分母が 0 の場合は、本当に必要な値を持っているかのように見せかけます。

于 2008-12-03T07:59:06.963 に答える
1

C# 10.0 以降では、次のことができます。

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct#parameterless-constructors-and-field-initializers

于 2021-09-21T13:47:59.080 に答える
1

C# を使用しているため、既定のコンストラクターを定義できません。

構造体は .NET で既定のコンストラクターを持つことができますが、それをサポートする特定の言語は知りません。

于 2008-12-03T19:53:48.893 に答える