13

C#言語仕様3.0のセクション10.13、デストラクタは次のように述べています。

デストラクタは継承されません。したがって、クラスには、そのクラスで宣言できるデストラクタ以外のデストラクタはありません。

C#プログラミングガイドのデストラクタセクションには、継承階層内のデストラクタがどのように呼び出されるかを示す例が含まれています。これには、次のステートメントが含まれます。

...クラスのデストラクタは自動的に呼び出され、最も派生したものから最も派生しなかったものへと順番に呼び出されます。

デストラクタを定義する基本クラス、基本クラスから継承し、デストラクタを定義しない派生クラスなど、さまざまな実用的な例でこれを調査しました。派生クラスのインスタンスを作成し、インスタンスへのすべての参照をスコープ外に出してからガベージコレクションを強制すると、派生クラスのインスタンスがファイナライズされたときに基本クラスで定義されたデストラクタが呼び出されることがわかります。

私の質問は、「デストラクタは継承されない」とは実際にはどういう意味ですか。デストラクタを明示的に呼び出すことはできませんが、継承チェーン内のデストラクタは自動的に呼び出され、派生クラスがデストラクタを定義していなくても基本クラスのデストラクタが呼び出されます。 ?

ファイナライズがC#言語/コンパイラではなくガベージコレクタによって実装されるという、いくつかの微妙な意味上の違いに関連していますか?

編集1:

C#言語仕様では、「インスタンスコンストラクターは継承されない」と規定されていますが、コンストラクターに関連する動作は記述子とは大幅に異なり、以下の例に示すように、「継承されない」用語でIMOに適合します。

  public class ConstructorTestBase
  {
    public ConstructorTestBase(string exampleParam)
    {
    }
  }

  public class ConstructorTest: ConstructorTestBase
  {
    public ConstructorTest(int testParam)
      : base(string.Empty)
    {
    }
  }

  ...

  // The following is valid since there is a derived class constructor defined that
  // accepts an integer parmameter.
  ConstructorTest test1 = new ConstructorTest(5);

  // The following is not valid since the base class constructor is not inherited
  // by the derived class and the derived class does not define a constructor that
  // takes a string parameter.
  ConstructorTest test2 = new ConstructorTest("Test"); 

次の例に示すように、デストラクタに関連する動作はこれとは大きく異なります。次の例では、基本クラスにのみ記述子を追加することで、前のコンストラクタの例を拡張しています。

  public class ConstructorTestBase
  {
    public ConstructorTestBase(string exampleParam)
    {
    }

    ~ConstructorTestBase()
    {
      Console.WriteLine("~ConstructorTestBase()");
    }
  }

  ...

  ConstructorTest test1 = new ConstructorTest(5);
  test1 = null;
  GC.Collect();

上記の例は、派生クラスがデストラクタを明示的に定義していなくても、派生クラスのインスタンスがファイナライズされたときに基本クラスコンストラクタが呼び出されることを示しています。

私の言いたいことは、これが何が起こっているのかを理解または理解していない多くの人々に出会ったということです。この理由の重要な部分は、「デストラクタは継承されない」というステートメントです。

編集2:

C#言語仕様には、次のことも記載されており、内部実装のコード例が示されています。

デストラクタは、System.Objectの仮想メソッドFinalizeをオーバーライドすることによって実装されます。C#プログラムでは、このメソッドをオーバーライドしたり、直接呼び出したり(またはオーバーライドしたり)することはできません。

ボンネット内の実装は、実際には、前述のように継承に基づいているため、私の質問は有効であり、これまでに受け取った応答のいずれも、質問に適切に対処していないと思います-何「デストラクタは継承されない」とは、実際にはどういう意味ですか?

4

1 に答える 1

19

それは勝つことの問題ではありません。私はあなたの質問を理解するために最善を尽くしていますが、あなたは完全に架空の攻撃から身を守ることに夢中になっているようです.

それはさておき、質問を 1 つずつ検討します。

私の質問は、デストラクタを明示的に呼び出すことはできませんが、継承チェーンのデストラクタは自動的に呼び出され、派生クラスがデストラクタを定義していなくても基本クラスのデストラクタが呼び出されるため、「デストラクタは継承されません」とは実際にはどういう意味ですか? ?

実際には、基本クラスのデストラクタは派生クラスのメンバーではないことを意味します。

私の反対の質問は、「(1) デストラクタを直接呼び出すことができない、(2) 継承チェーン内のデストラクタはコレクション時に自動的に呼び出される、(3) 基本クラスのデストラクタは、派生クラスはコンストラクタを定義していませんが、基本クラスのデストラクタが派生クラスのメンバーであるかどうかの問題に関係がありますか?

ファイナライズが C# 言語/コンパイラではなく、ガベージ コレクターによって実装されるという微妙な意味上の違いに関連していますか?

いいえ。

C# 言語は仕様です。何も実装しません。C# コンパイラは仕様を実装します。確かに「ファイナライズを実装」しません。CLR はファイナライズを実装するものです。

いずれにしても、基本クラスのデストラクターが派生クラスのメンバーではないという事実は、CLR ガベージ コレクターによるファイナライズの実装方法とは何の関係もありません。

最後に、CLR ファイナライズ セマンティクスの問題と、それらが継承の問題に関連しない理由に戻ります。

C# 言語仕様には「インスタンス コンストラクターは継承されない」と記載されていますが、コンストラクターに関連する動作はデストラクターとは大きく異なり、IMO は「継承されない」という用語により適しています。

OK、あなたがそう信じていることを認めます。これらの行動が継承の問題に関連しているとなぜ思いますか? なぜあなたがそれを信じるのか、私は少しも理解していません。継承に関連するのは、問題のエンティティが基本型のメンバーであるという理由だけで、派生型のメンバーであるかどうかです。

基本型のコンストラクターを、そのコンストラクターを欠く派生型の構築を介して直接呼び出すことができないという事実は、コンストラクターが継承されないという事実と一致することに同意します。これが、デストラクタが継承されるかどうかという問題とどのように密接に関係しているのかわかりません。

より密接な事実は、パブリック パラメーターなしのコンストラクターを持つ基本クラスは、パブリック パラメーターなしのコンストラクターを欠く派生クラスがその既定のコンストラクターで構築されるときに呼び出されるコンストラクターを持っているということです。派生クラスは、基本クラスのパブリック パラメーターなしのコンストラクターを継承しませんが、それでも呼び出されます。継承されていないコンストラクターがこの方法で呼び出されることを受け入れるのであれば、デストラクタも同様のセマンティクスで呼び出されることを受け入れてみませんか?

私の要点は、これが起こることを認識していない、または理解していない多くの人々に遭遇したことです。これの理由の重要な部分は、「デストラクタは継承されない」ステートメントです。

私は完全に同意します。正しいセマンティクスが何であるかを正確に理解していない場合は、おそらくデストラクタを書くべきではありません。デストラクタを初心者に適したトピックとして扱っている C# 初心者向けの本の数にがっかりしています。正しいデストラクタを記述することは、C# で最も困難な基本タスクの 1 つです。

上記のように、内部実装は実際には継承に基づいているため、私の質問は有効だと思います

あなたの質問は関係なく完全に有効です。しかし今では、機能の実装の詳細を機能の仕様と混同しています。

類推して、匿名型を考えてみましょう。当然のことながら、彼らには名前がありません。しかし、私たちが吐き出すメタデータはもちろん型の名前です。CLRでは、型に名前が必要です。それは矛盾ですか?あまり。仕様で「匿名型」と呼ばれる概念的なエンティティには名前がなく、それが重要です。もちろん、実装者は、すべての型の名前付けを必要としないプラットフォームを自由に使用できます。

同様に、C# の実装では、Finalize という仮想メソッドをオーバーライドするメソッドに IL を吐き出すことでデストラクタを実装しています。C# 言語は、ランタイムのこの機能に依存しないように慎重に設計されています。C# の別の実装では、デストラクタを実装する他のメカニズムを完全に自由に選択できます。(仕様を変更して、デストラクタがどのように実装されるかについての情報は実装の詳細であり、C# 言語の要件ではないことをより明確にすることをお勧めします。)

ただし、実装の詳細の選択に関係なく、基本クラスのデストラクターは派生クラスのメンバーではありません。実装の詳細に関係なく、匿名型には名前がありません。

これで明確になりましたか、それとも他に質問がありますか?

于 2009-12-09T21:30:43.723 に答える