この動作の違い、特に構造体の場合の動作は驚くべきものであり、直感的ではありません-何が起こっているのでしょうか? stuct コンストラクトのデフォルトの引数は何に役立ちますか? 役に立たない場合、なぜこれをコンパイルさせますか?
それは何の役にも立ちません。発行された IL コードは、デフォルト パラメーターを使用してコンストラクターへの呼び出しを生成しませんが、default(Test)
. コンパイラーがコンストラクターが呼び出されないという警告を発することは完全に合理的と思われます (これは実装の詳細ですが)。http://connect.microsoft.comで問題を報告します
生成された IL コードを見ると、次のようになります。
Test myTest = new Test();
bool valid = myTest.IsValid;
見てみましょう:
IL_0000: ldloca.s 00 // myTest
IL_0002: initobj UserQuery.Test // default(Test);
IL_0008: ldloca.s 00 // myTest
IL_000A: call UserQuery+Test.get_IsValid
IL で行われた呼び出しは、コンストラクターへのメソッド呼び出しではなく (次のようになります: call Test..ctor
)、次の呼び出しを生成したことに注意してくださいinitobj
。
指定されたアドレスにある値型の各フィールドを null 参照または適切なプリミティブ型の 0 に初期化します。Newobjとは異なり、initobjはコンストラクター メソッドを呼び出しません。initobj は値型の初期化を目的としており、newobj はオブジェクトの割り当てと初期化に使用されます。
これは、C#-6.0 まではそのようなコンストラクターを宣言することは禁止されていたため、コンパイラーがデフォルト パラメーターを持つコンストラクターを単に無視していることを意味します。
@JonSkeet は、構造体で "new" を使用するとヒープまたはスタックに割り当てられますか?への回答でこれを深く掘り下げています。
編集:
私は実際にMads Torgersonに、関連していると思われる C#-6.0 でのパラメーターなしのコンストラクターの新しい使用法について質問したところ、彼は次のように述べました。
@Yuval など、構造体のパラメーターなしのコンストラクターに関して: 認識すべきことは、以前も現在も、コンストラクターは必ずしも構造体で実行されるとは限らないということです。私たちが行ったのは、実行が保証されないパラメーターなしのコンストラクターを持つ機能を追加したことだけです。初期化されていることが保証されている構造体を持つ合理的な方法はなく、パラメーターなしのコンストラクターはそれを助けません。
パラメーターなしのコンストラクターが役立つのは、パラメーターなしのコンストラクターを使用できるようにすることです。
混乱の主な原因は、 'new S()' が 'default(S)' を意味することが許されていることだと思います。それはこの言語の歴史的な間違いであり、私はそれを取り除けることを心から願っています. パラメーターなしのコンストラクターを持たない構造体で 'new S()' を使用することを強く思いとどまらせます。私が知る限り、C# 1.0 には default(S) 構文が存在しなかったため、これは含まれているため、これは構造体の既定値を取得するために使用される構文に過ぎませんでした。
T が構造体である場合、コンパイラの実装ではこれまでのところ、'new T()' が本質的に default(T) を意味するように「最適化」されていることは事実です。これは実際にはバグでした.ILで許可されているため、実際のパラメーターなしのコンストラクターがある場合は常に呼び出すことになっていました. これを修正して、一般的な場合でもコンストラクターを呼び出すようにします。
したがって、セマンティクスはクリーンです。 new S() は、構造体でパラメーターなしのコンストラクターを実行する唯一の方法であり、ジェネリックを介しても常にそのコンストラクターを実行します。