web-dev-qa-db-ja.com

構造体がデストラクタを持つことができないのはなぜですか?

あなたが思うそのような質問に対するインタビューの最良の答えは何ですか?

ここでコピーが見つからなかったと思います。ある場合はリンクしてください。

23
Valentin Kuzub

これを見る別の方法-構造体はデストラクタを持つことができない/持たないという仕様を引用するのではなく-仕様が変更された場合にどうなるかを考えてください-むしろ、質問してみましょう:can推測なぜ言語設計者は、そもそも構造体に「デストラクタ」を持たせないことを決定したのでしょうか?

(ここでは、Wordの「デストラクタ」にこだわらないでください。基本的には、変数がスコープ外になると自動的に呼び出される構造体の魔法のメソッドについて話します。つまり、C++のデストラクタに類似した言語機能です。 )

最初に気付くのは、メモリの解放は気にしないということです。オブジェクトがスタック上にあるかヒープ上にあるか(たとえば、クラスの構造体)、メモリは遅かれ早かれ何らかの方法で処理されます。スタックからポップされるか、収集されます。そもそもデストラクタのようなものを使用する本当の理由は、管理するためです外部リソース-ファイルハンドル、ウィンドウハンドル、またはCLRをクリーンアップするために特別な処理が必要なその他のものそれ自体は知りません。

ここで、このクリーンアップを実行できるデストラクタを持つことを構造体に許可するとします。結構です。構造体がパラメーターとして渡されると、値によって渡されることに気付くまで、それらはコピーされます。これで、同じ内部フィールドを持つtwo構造体ができました。これらは、both同じオブジェクトのクリーンアップを試みます。一方が最初に発生するため、後でもう一方を使用しているコードが不思議なことに失敗し始めます...そしてそれ自体のクリーンアップが失敗します(うまくいけば!-最悪の場合、他のランダムなリソースのクリーンアップに成功する可能性があります-これは可能ですたとえば、ハンドル値が再利用される状況で発生します。)

'デストラクタ'が実行されないように、パラメータである構造体に特別なケースを作成することもできます(ただし、注意してください。関数を呼び出すときは、実際のリソースを '所有する'のは常に外側の構造体であることに注意してください。 -したがって、一部の構造体は他の構造体と微妙に異なります...)-しかし、通常の構造体変数ではこの問題が発生し、ある構造体を別の構造体に割り当ててコピーを作成できます。

割り当て操作に特別なメカニズムを追加することでこれを回避できる可能性があります。これにより、新しい構造体が基になるリソースの所有権を新しいコピーとネゴシエートできるようになります。おそらく、リソースを共有したり、古いものから新しいものに所有権を完全に譲渡したりできます。本質的にはC++ランドに向かいました。そこでは、コピーコンストラクター、代入演算子が必要であり、知らない初心者プログラマーを捕らえるのを待っている微妙な点がたくさん追加されています。また、C#の全体的なポイントは、この種のC++スタイルの複雑さを可能な限り回避することであることに注意してください。

そして、もう少し混乱させるために、他の回答の1つが指摘しているように、構造体はローカルオブジェクトとして存在するだけではありません。地元の人にとって、スコープは素晴らしく、明確に定義されています。ただし、構造体はクラスオブジェクトのメンバーになることもできます。その場合、「デストラクタ」はいつ呼び出されるべきですか?もちろん、コンテナクラスが完成したときにそれを行うことができます。しかし、これで、構造体が存在する場所に応じて動作が大きく異なるメカニズムができました。構造体がローカルの場合、スコープの最後ですぐにトリガーされます。構造体がクラス内にある場合、遅延してトリガーされます...したがって、構造体の1つにあるリソースが特定の時間にクリーンアップされるようにすることに本当に関心があり、構造体が最終的にメンバーになる可能性がある場合クラスの場合、ベースを確実にカバーするには、IDisposable/using()のような明示的なものが必要になるでしょう。

したがって、言語設計者のために話すことはできませんが、そのような機能を含めないことにした理由の1つは、ワームの缶であり、C#を適度に単純に保ちたいと考えていたためだと推測できます。 。

52
BrendanMcK

から ジョンジャガー

「構造体にデストラクタを含めることはできません。デストラクタは、偽装したobject.Finalizeのオーバーライドであり、値型である構造体はガベージコレクションの対象ではありません。」

31
Cameron Skinner

配列と文字列以外のすべてのオブジェクトは、同じ方法でヒープに格納されます。「オブジェクト関連」プロパティ(そのタイプ、アクティブなモニターロックで使用されているかどうか、抑制されていないものがあるかどうか)に関する情報を提供するヘッダーです。 Finalizeメソッドなど)、およびそのデータ(すべてのタイプのインスタンスフィールド(パブリック、プライベート、および保護された混合、基本クラスフィールドは派生タイプフィールドの前に表示されます)のコンテンツを意味します。すべてのヒープオブジェクトにはヘッダーがあるため、システムは任意のオブジェクトへの参照を取得し、それが何であるか、およびガベージコレクターがそれをどのように処理するかを知ることができます。システムに作成されたすべてのオブジェクトのリストがあり、Finalizeメソッドがある場合、システムは調べることができます。リスト内のすべてのオブジェクトについて、そのFinalizeメソッドが抑制されていないかどうかを確認し、適切に処理します。

構造体はヘッダーなしで保存されます。 2つの整数フィールドを持つPointのような構造体は、単に2つの整数として格納されます。構造体にrefを設定することは可能ですが(このようなものは、構造体がrefパラメーターとして渡されるときに作成されます)、refを使用するコードは、refが指す構造体のタイプを認識している必要があります。これは、refまた、構造体自体もその情報を保持していません。さらに、ヒープオブジェクトはガベージコレクターによってのみ作成できます。これにより、作成されたオブジェクトは次のGCサイクルまで常に存在することが保証されます。対照的に、ユーザーコードはそれ自体で(多くの場合スタック上で)構造体を作成および破棄できます。コードがrefとともに構造体を作成し、そのrefを呼び出されたルーチンに渡す場合、呼び出されたルーチンが戻るまで、コードが構造体を破棄する(または、さらに言えば、何かを行う)方法はありません。 structは、少なくとも呼び出されたルーチンが終了するまで存在することが保証されています。一方、呼び出されたルーチンが終了すると、呼び出し元はその後いつでも構造体を自由に破棄できるため、指定されたrefは無効であると見なされます。

0
supercat