2017-02-16 59 views
1

在瀏覽與通過SMTP發送電子郵件相關的一些代碼時,我在MSDN中收到了以下代碼片斷。爲什麼破壞在這裏被明確地調用?

static void CreateMessageWithAttachment(String^ server) 
{ 
    String^ file = L"data.xls"; 

    MailMessage^ message = gcnew MailMessage(L"[email protected]",L"[email protected]",L"Quarterly data report.",L"See the attached spreadsheet."); 

    Attachment^ data = gcnew Attachment(file, MediaTypeNames::Application::Octet); 

    ContentDisposition^ disposition = data->ContentDisposition; 
    disposition->CreationDate = System::IO::File::GetCreationTime(file); 
    disposition->ModificationDate = System::IO::File::GetLastWriteTime(file); 
    disposition->ReadDate = System::IO::File::GetLastAccessTime(file); 

    message->Attachments->Add(data); 

    SmtpClient^ client = gcnew SmtpClient(server); 

    client->Credentials = CredentialCache::DefaultNetworkCredentials; 
    client->Send(message); 

    data->~Attachment(); 
    client->~SmtpClient(); 
} 

我只是想知道他們爲什麼在這裏調用析構函數?我在這裏錯過了什麼嗎?

data->~Attachment(); 
client->~SmtpClient(); 

回答

2

在C++/CLI中,ref類析構函數是在Dispose pattern的抽象。

下面的C++/CLI類,當編譯:

public ref class Test 
{ 
public: 
    Test() { System::Console::WriteLine("ctor"); } 
    ~Test() { System::Console::WriteLine("dtor"); } 

    static void Foo() 
    { 
     auto foo = gcnew Test(); 
     foo->~Test(); 
    } 
}; 

反編譯下面的C#代碼(C#語義更接近底層的IL代碼,因此它的可視化會發生什麼好辦法):

public class Test : IDisposable 
{ 
    public Test() 
    { 
     Console.WriteLine("ctor"); 
    } 

    private void ~Test() 
    { 
     Console.WriteLine("dtor"); 
    } 

    public static void Foo() 
    { 
     new Test().Dispose(); 
    } 

    protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool A_0) 
    { 
     if (A_0) 
     { 
      this.~Test(); 
     } 
     else 
     { 
      this.Finalize(); 
     } 
    } 

    public virtual void Dispose() 
    { 
     this.Dispose(true); 
     GC.SuppressFinalize((object)this); 
    } 
} 

你可以看到配置模式是由編譯器自動實現的。

~Test「析構函數」被編譯爲私有方法,並且爲您生成了一個實現IDisposable::Dispose。由於某種原因,編譯器也會調用(空)終結器。

此外,正如您在靜態Foo方法中所看到的,foo->~Test();僅被轉換爲致電Dispose。編譯器不會讓你直接調用foo->Dispose();

標準的方法來調用「析構函數」(並因此Dispose方法)是使用delete關鍵字:delete foo;是相同的C/CLI foo->~Test();foo時是一個託管句柄。

注意,在這個例子中,而不是寫:

auto foo = gcnew CppCli::Test(); 
foo->Whatever(); 
delete foo; 

你可以使用堆棧的語義和寫:

Test foo; 
foo.Whatever(); 

foo.~Test();會時foo超出範圍,就像定期調用C++。


爲了完整起見,下面介紹整個事情是如何與終結器交互的。讓我們添加一個:

public ref class Test 
{ 
public: 
    Test() { System::Console::WriteLine("ctor"); } 
    ~Test() { System::Console::WriteLine("dtor"); } 
    !Test() { System::Console::WriteLine("finalizer"); } 
}; 

這反編譯以下類似C#代碼:

public class Test : IDisposable 
{ 
    public Test() 
    { 
     Console.WriteLine("ctor"); 
    } 

    // This is the real finalizer 
    ~Test() 
    { 
     this.Dispose(false); 
    } 

    // This is what C++/CLI compiles ~Test to 
    // Let's call this MethodA 
    private void ~Test() 
    { 
     Console.WriteLine("dtor"); 
    } 

    // This is what C++/CLI compiles !Test to 
    // Let's call this MethodB 
    private void !Test() 
    { 
     Console.WriteLine("finalizer"); 
    } 

    [HandleProcessCorruptedStateExceptions] 
    protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool A_0) 
    { 
     if (A_0) 
     { 
      this.~Test(); // MethodA, NOT the finalizer 
     } 
     else 
     { 
      try 
      { 
       this.!Test(); // MethodB 
      } 
      finally 
      { 
       base.Finalize(); 
      } 
     } 
    } 

    public virtual void Dispose() 
    { 
     this.Dispose(true); 
     GC.SuppressFinalize((object)this); 
    } 
} 

需要注意的是,爲了增加混亂,在C#中的終結是~Test(),這是不同private void ~Test()函數,C++/CLI編譯器爲您的析構函數生成。

+0

非常感謝您的詳細解釋,真的很有幫助。 – Geek

+1

你也許應該注意''Test()'語法不是標準的。處理託管對象的規範方式是使用'delete',與C++對象一樣。 –

+0

@DavidYaw oops,你是對的,這是一個重要的觀點,謝謝! –

相關問題