他の回答者が指摘しているように、あなたは正しいです。廃棄コードが異なる理由は、C ++/CLIだからです。
C ++ / CLIは、クリーンアップコードの記述に別のイディオムを使用します。
- C#:Dispose()と〜ClassName()(ファイナライザー)はどちらもDispose(bool)を呼び出します。
- 3つのメソッドはすべて、開発者によって作成されています。
- C ++ / CLI:Dispose()とFinalize()はどちらもDispose(bool)を呼び出し、Dispose(bool)は〜ClassName()または!ClassName()(それぞれデストラクタとファイナライザー)を呼び出します。
- 〜ClassName()と!ClassName()は開発者によって書かれています。
- お気づきのとおり、〜ClassName()はC#とは異なる方法で処理されます。C ++ / CLIでは、「〜ClassName」という名前のメソッドとして残りますが、C#の〜ClassName()はとしてコンパイルされ
protected override void Finalize()
ます。
- Dispose()、Finalize()、およびDispose(bool)は、コンパイラーによってのみ記述されます。そうすることで、コンパイラーは通常は想定されていないことを実行します。
実例を示すために、単純なC ++/CLIクラスを次に示します。
public ref class TestClass
{
~TestClass() { Debug::WriteLine("Disposed"); }
!TestClass() { Debug::WriteLine("Finalized"); }
};
これがReflectorからの出力で、C#構文に逆コンパイルされています。
public class TestClass : IDisposable
{
private void !TestClass() { Debug.WriteLine("Finalized"); }
private void ~TestClass() { Debug.WriteLine("Disposed"); }
public sealed override void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
[HandleProcessCorruptedStateExceptions]
protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool disposing)
{
if (disposing)
{
this.~TestClass();
}
else
{
try
{
this.!TestClass();
}
finally
{
base.Finalize();
}
}
}
protected override void Finalize()
{
this.Dispose(false);
}
}
編集
C ++ / CLIはC#よりもコンストラクターの例外をうまく処理しているようです。
C ++ / CLIとC#の両方でテストアプリを作成しました。これらは、ParentクラスとChildクラスを定義し、Childクラスのコンストラクターが例外をスローします。どちらのクラスにも、コンストラクター、disposeメソッド、およびファイナライザーからのデバッグ出力があります。
C ++ / CLIでは、コンパイラーは子コンストラクターの内容をtry / faultブロックにラップし、親のDisposeメソッドをfaultで呼び出します。(フォールトコードは、例外が他のtry / catchブロックによってキャッチされたときに実行されると思います。これは、スタックを上に移動する前にすぐに実行されるcatchまたはfinallyブロックとは対照的です。しかし、そこには微妙な点が欠けている可能性があります。 。)C#では、暗黙のcatchまたはfaultブロックがないため、Parent.Dispose()が呼び出されることはありません。GCがオブジェクトの収集に取り掛かると、両方の言語が子と親の両方のファイナライザーを呼び出します。
これが私がC++/CLIでコンパイルしたテストアプリです:
public ref class Parent
{
public:
Parent() { Debug::WriteLine("Parent()"); }
~Parent() { Debug::WriteLine("~Parent()"); }
!Parent() { Debug::WriteLine("!Parent()"); }
};
public ref class Child : public Parent
{
public:
Child() { Debug::WriteLine("Child()"); throw gcnew Exception(); }
~Child() { Debug::WriteLine("~Child()"); }
!Child() { Debug::WriteLine("!Child()"); }
};
try
{
Object^ o = gcnew Child();
}
catch(Exception^ e)
{
Debug::WriteLine("Exception Caught");
Debug::WriteLine("GC::Collect()");
GC::Collect();
Debug::WriteLine("GC::WaitForPendingFinalizers()");
GC::WaitForPendingFinalizers();
Debug::WriteLine("GC::Collect()");
GC::Collect();
}
出力:
親()
子()
タイプ「System.Exception」の最初のチャンスの例外がCppCLI-DisposeTest.exeで発生しました
〜Parent()
例外が発生しました
GC :: Collect()
GC :: WaitForPendingFinalizers()
!子()
!親()
GC :: Collect()
Reflectorの出力を見ると、C ++ / CLIコンパイラがChildコンストラクタをコンパイルした方法(C#構文に逆コンパイル)があります。
public Child()
{
try
{
Debug.WriteLine("Child()");
throw new Exception();
}
fault
{
base.Dispose(true);
}
}
比較のために、C#の同等のプログラムを次に示します。
public class Parent : IDisposable
{
public Parent() { Debug.WriteLine("Parent()"); }
public virtual void Dispose() { Debug.WriteLine("Parent.Dispose()"); }
~Parent() { Debug.WriteLine("~Parent()"); }
}
public class Child : Parent
{
public Child() { Debug.WriteLine("Child()"); throw new Exception(); }
public override void Dispose() { Debug.WriteLine("Child.Dispose()"); }
~Child() { Debug.WriteLine("~Child()"); }
}
try
{
Object o = new Child();
}
catch (Exception e)
{
Debug.WriteLine("Exception Caught");
Debug.WriteLine("GC::Collect()");
GC.Collect();
Debug.WriteLine("GC::WaitForPendingFinalizers()");
GC.WaitForPendingFinalizers();
Debug.WriteLine("GC::Collect()");
GC.Collect();
return;
}
そしてC#出力:
親()
子()
CSharp-DisposeTest.exeでタイプ「System.Exception」の最初のチャンスの例外が発生しました
例外が発生しました
GC :: Collect()
GC :: WaitForPendingFinalizers()
〜Child()
〜Parent()
GC :: Collect()