2017-02-16 3 views
1

При просмотре кода, связанного с отправкой E-mail через SMTP, я получил следующий фрагмент в MSDN.Почему destruct вызывается здесь явно?

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, A 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 «destructor» скомпилирован для частного метода, и для вас создана реализация IDisposable::Dispose. По какой-то причине компилятор также вызывает (пустой) финализатор.

Также, как вы можете видеть в статическом методе Foo, foo->~Test(); просто переведен на звонок Dispose. Компилятор не позволит вам напрямую позвонить foo->Dispose();.

Но стандарт подход называют «деструктор» (и, следовательно, метод Dispose) заключается в использовании delete ключевого слова: delete foo; такого же, как foo->~Test(); в C++/CLI, когда 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# -like код:

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, вы правы, это важный момент, спасибо! –

Смежные вопросы