Menerapkan IDisposable dengan benar


145

Di kelas saya, saya menerapkan IDisposable sebagai berikut:

public class User : IDisposable
{
    public int id { get; protected set; }
    public string name { get; protected set; }
    public string pass { get; protected set; }

    public User(int UserID)
    {
        id = UserID;
    }
    public User(string Username, string Password)
    {
        name = Username;
        pass = Password;
    }

    // Other functions go here...

    public void Dispose()
    {
        // Clear all property values that maybe have been set
        // when the class was instantiated
        id = 0;
        name = String.Empty;
        pass = String.Empty;
    }
}

Di VS2012, Analisis Kode saya mengatakan untuk mengimplementasikan IDisposable dengan benar, tapi saya tidak yakin apa yang saya lakukan salah di sini.
Teks yang tepat adalah sebagai berikut:

CA1063 Implement IDisposable dengan benar Berikan implementasi Buang (bool) yang dapat ditimpa pada 'Pengguna' atau tandai jenisnya sebagai disegel. Panggilan untuk Buang (salah) seharusnya hanya membersihkan sumber daya asli. Panggilan untuk Buang (benar) harus membersihkan sumber daya yang dikelola dan asli. stman User.cs 10

Untuk referensi: CA1063: Terapkan IDisposable dengan benar

Saya sudah membaca halaman ini, tetapi saya rasa saya tidak begitu mengerti apa yang perlu dilakukan di sini.

Jika ada yang bisa menjelaskan dalam istilah yang lebih panjang tentang apa masalahnya dan / atau bagaimana IDisposable harus dilaksanakan, itu akan sangat membantu!


1
Apakah itu semua kode di dalamnya Dispose?
Claudio Redi

42
Anda harus menerapkan metode Buang () untuk memanggil metode Buang () pada salah satu anggota kelas Anda. Tak satu pun dari anggota itu yang memilikinya. Karena itu Anda tidak boleh menerapkan IDisposable. Menyetel ulang nilai properti tidak ada gunanya.
Hans Passant

13
Anda hanya perlu untuk mengimplementasikan IDispoablejika Anda memiliki sumber daya unmanaged untuk membuang (termasuk sumber daya unmanaged yang dibungkus ( SqlConnection, FileStream, dll). Anda tidak dan tidak harus menerapkan IDisposablejika Anda hanya memiliki sumber daya dikelola seperti di sini. Hal ini, IMO, masalah besar dengan analisis kode. Ini sangat bagus dalam memeriksa aturan-aturan kecil yang konyol, tetapi tidak pandai memeriksa kesalahan konseptual.
jason

51
Sangat mengecewakan bagi saya bahwa beberapa orang lebih suka downvote dan melihat pertanyaan ini ditutup daripada upaya untuk membantu orang yang jelas telah salah paham konsep. Sayang sekali.
Ortund

2
Jadi jangan downvote, jangan upvote, tinggalkan pos di nol dan tutup pertanyaan dengan pointer yang bermanfaat.
tjmoore

Jawaban:


113

Ini akan menjadi implementasi yang benar, meskipun saya tidak melihat apa pun yang Anda butuhkan untuk membuang kode yang Anda posting. Anda hanya perlu menerapkan IDisposableketika:

  1. Anda memiliki sumber daya yang tidak dikelola
  2. Anda berpegangan pada referensi hal-hal yang dapat dibuang sendiri.

Tidak ada dalam kode yang Anda poskan yang perlu dibuang.

public class User : IDisposable
{
    public int id { get; protected set; }
    public string name { get; protected set; }
    public string pass { get; protected set; }

    public User(int userID)
    {
        id = userID;
    }
    public User(string Username, string Password)
    {
        name = Username;
        pass = Password;
    }

    // Other functions go here...

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

    protected virtual void Dispose(bool disposing)
    {
        if (disposing) 
        {
            // free managed resources
        }
        // free native resources if there are any.
    }
}

2
Saya diberitahu ketika saya mulai menulis dalam C # bahwa yang terbaik adalah memanfaatkan using(){ }kapan pun memungkinkan, tetapi untuk melakukan itu, Anda perlu mengimplementasikan IDisposable, jadi secara umum, saya lebih suka mengakses kelas melalui usings, esp. jika saya hanya perlu kelas dalam satu atau dua fungsi
Ortund

62
@Ortund Anda salah paham. Cara terbaik adalah menggunakan usingblok ketika kelas mengimplementasikan IDisposable . Jika Anda tidak perlu kelas untuk sekali pakai, jangan terapkan itu. Tidak ada gunanya.
Daniel Mann

5
@DanielMann Semantik dari sebuah usingblok memang cenderung menarik di luar IDisposableantarmuka saja. Saya membayangkan ada lebih dari beberapa penyalahgunaan IDisposablehanya untuk tujuan pelingkupan.
Thomas

1
Sama seperti catatan tambahan jika Anda akan memiliki sumber daya yang tidak dikelola untuk dibebaskan, Anda harus menyertakan Finalizer calling Buang (false), yang akan memungkinkan GC memanggil Finalizer ketika melakukan pengumpulan sampah (jika Buang tidak dipanggil lagi) dan benar-benar gratis tanpa pengelolaan sumber daya.
mariozski

4
Tanpa finalizer dalam pemanggilan implementasi Anda GC.SuppressFinalize(this);tidak ada gunanya. Seperti @mariozski menunjukkan finalizer akan membantu untuk memastikan bahwa Disposedipanggil sama sekali jika kelas tidak digunakan di dalam usingblok.
Haymo Kutschbach

57

Pertama-tama, Anda tidak perlu "membersihkan" stringdan int- mereka akan dijaga secara otomatis oleh pengumpul sampah. Satu-satunya hal yang perlu dibersihkan Disposeadalah sumber daya yang tidak dikelola atau sumber daya yang dikelola yang menerapkan IDisposable.

Namun, dengan asumsi ini hanya latihan pembelajaran, cara yang disarankan untuk diterapkan IDisposableadalah menambahkan "tangkapan keselamatan" untuk memastikan bahwa setiap sumber daya tidak dibuang dua kali:

public void Dispose()
{
    Dispose(true);

    // Use SupressFinalize in case a subclass 
    // of this type implements a finalizer.
    GC.SuppressFinalize(this);   
}
protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        if (disposing) 
        {
            // Clear all property values that maybe have been set
            // when the class was instantiated
            id = 0;
            name = String.Empty;
            pass = String.Empty;
        }

        // Indicate that the instance has been disposed.
        _disposed = true;   
    }
}

3
+1, memiliki bendera untuk memastikan kode pembersihan dijalankan hanya sekali adalah jalan, jauh lebih baik daripada menyetel properti menjadi nol atau apa pun (terutama karena itu mengganggu readonlysemantik)
Thomas

+1 untuk menggunakan kode pengguna (meskipun akan dibersihkan secara otomatis) untuk memperjelas apa yang terjadi di sana. Juga, karena tidak menjadi pelaut asin dan memukulnya karena membuat kesalahan kecil sambil belajar seperti banyak orang lain di sini.
Murphybro2

42

Contoh berikut menunjukkan praktik umum terbaik untuk mengimplementasikan IDisposableantarmuka. Referensi

Perlu diingat bahwa Anda memerlukan destructor (finalizer) hanya jika Anda memiliki sumber daya yang tidak dikelola di kelas Anda. Dan jika Anda menambahkan destruktor, Anda harus menekan Finalisasi di Buang , jika tidak maka objek Anda akan berada di memori selama dua siklus sampah (Catatan: Baca cara kerja Finalisasi ). Contoh di bawah ini menguraikan semua di atas.

public class DisposeExample
{
    // A base class that implements IDisposable. 
    // By implementing IDisposable, you are announcing that 
    // instances of this type allocate scarce resources. 
    public class MyResource: IDisposable
    {
        // Pointer to an external unmanaged resource. 
        private IntPtr handle;
        // Other managed resource this class uses. 
        private Component component = new Component();
        // Track whether Dispose has been called. 
        private bool disposed = false;

        // The class constructor. 
        public MyResource(IntPtr handle)
        {
            this.handle = handle;
        }

        // Implement IDisposable. 
        // Do not make this method virtual. 
        // A derived class should not be able to override this method. 
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method. 
            // Therefore, you should call GC.SupressFinalize to 
            // take this object off the finalization queue 
            // and prevent finalization code for this object 
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios. 
        // If disposing equals true, the method has been called directly 
        // or indirectly by a user's code. Managed and unmanaged resources 
        // can be disposed. 
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed. 
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called. 
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources. 
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up 
                // unmanaged resources here. 
                // If disposing is false, 
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;

            }
        }

        // Use interop to call the method necessary 
        // to clean up the unmanaged resource.
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);

        // Use C# destructor syntax for finalization code. 
        // This destructor will run only if the Dispose method 
        // does not get called. 
        // It gives your base class the opportunity to finalize. 
        // Do not provide destructors in types derived from this class.
        ~MyResource()
        {
            // Do not re-create Dispose clean-up code here. 
            // Calling Dispose(false) is optimal in terms of 
            // readability and maintainability.
            Dispose(false);
        }
    }
    public static void Main()
    {
        // Insert code here to create 
        // and use the MyResource object.
    }
}

14

IDisposableada untuk menyediakan sarana bagi Anda untuk membersihkan tidak terkelola sumber daya yang tidak yang tidak akan dibersihkan secara otomatis oleh Pengumpul Sampah.

Semua sumber daya yang Anda "bersihkan" adalah sumber daya yang dikelola, dan dengan demikian Disposemetode Anda tidak menghasilkan apa-apa. Kelas Anda seharusnya tidak diimplementasikan IDisposablesama sekali. Pengumpul Sampah akan mengurus semua bidang itu dengan baik sendiri.


1
Setuju dengan ini - ada gagasan membuang segala sesuatu padahal sebenarnya tidak diperlukan. Buang harus digunakan hanya jika Anda memiliki sumber daya yang tidak dikelola untuk dibersihkan !!
Chandramouleswaran Ravichandra

4
Tidak sepenuhnya benar, metode Buang juga memungkinkan Anda untuk membuang "sumber daya yang dikelola yang menerapkan IDisposable"
Matt Wilko

@MattWilko Itu menjadikannya cara tidak langsung untuk membuang sumber daya yang tidak dikelola, karena memungkinkan sumber daya lain untuk membuang sumber daya yang tidak dikelola. Di sini bahkan tidak ada referensi tidak langsung ke sumber daya yang tidak dikelola melalui sumber daya yang dikelola lainnya.
Servy

@MattWilko Buang akan secara otomatis memanggil sumber daya yang Dikelola yang menerapkan IDesposable
sharma

@pankysharma Tidak, tidak akan. Itu perlu secara manual dipanggil. Itulah intinya. Anda tidak dapat menganggap itu akan secara otomatis dipanggil, Anda hanya tahu orang-orang seharusnya menyebutnya secara manual, tetapi orang-orang memang membuat kesalahan dan lupa.
Servy

14

Anda perlu menggunakan Pola Pakai seperti ini:

private bool _disposed = false;

protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        if (disposing)
        {
            // Dispose any managed objects
            // ...
        }

        // Now disposed of any unmanaged objects
        // ...

        _disposed = true;
    }
}

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

// Destructor
~YourClassName()
{
    Dispose(false);
}

1
bukankah lebih bijaksana untuk memiliki panggilan ke GC.SuppressFinalize (ini) di destructor juga? Kalau tidak, objek itu sendiri akan direklamasi di GC berikutnya
Sudhanshu Mishra

2
@dnetnetguy: Object destructor dipanggil saat gc berjalan. Jadi menelepon dua kali tidak mungkin. Lihat di sini: msdn.microsoft.com/en-us/library/ms244737.aspx
schoetbi

1
Jadi sekarang apakah kita menyebut kode boilerplate sebagai "pola"?
Chel

4
Oh tidak, kami tidak. MSDN menyatakan bahwa itu ADALAH pola "Buang Pola" di sini - msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx jadi sebelum memilih, mungkin Google sedikit?
Belogix

2
Baik Microsoft maupun pos Anda tidak dengan jelas menyatakan mengapa polanya harus seperti ini. Secara umum, ini bahkan bukan boilerplate, itu hanya berlebihan - digantikan oleh SafeHandle(dan sub tipe). Dalam hal sumber daya yang dikelola, penerapan pembuangan yang benar menjadi jauh lebih sederhana; Anda dapat memotong kode menjadi implementasi sederhana dari void Dispose()metode ini.
BatteryBackupUnit

10

Anda tidak perlu melakukan Anda Usermakhluk kelas IDisposablesejak kelas tidak memperoleh sumber daya non-dikelola (file, koneksi database, dll). Biasanya, kami menandai kelas seolah- IDisposableolah mereka memiliki setidaknya satu IDisposablebidang atau / dan properti. Saat menerapkan IDisposable, lebih baik letakkan menurut skema tipikal Microsoft:

public class User: IDisposable {
  ...
  protected virtual void Dispose(Boolean disposing) {
    if (disposing) {
      // There's no need to set zero empty values to fields 
      // id = 0;
      // name = String.Empty;
      // pass = String.Empty;

      //TODO: free your true resources here (usually IDisposable fields)
    }
  }

  public void Dispose() {
    Dispose(true);

    GC.SuppressFinalize(this);
  } 
}

Itu biasanya terjadi. Namun di sisi lain, konstruk penggunaan membuka kemungkinan untuk menulis sesuatu yang mirip dengan C ++ smart pointer, yaitu objek yang akan mengembalikan keadaan sebelumnya tidak peduli bagaimana blok penggunaan keluar. Satu-satunya cara saya menemukan melakukan ini adalah membuat objek mengimplementasikan IDisposable. Tampaknya saya bisa mengabaikan peringatan kompiler dalam kasus penggunaan marjinal.
Papa Smurf

3

Idisposable diimplementasikan setiap kali Anda ingin pengumpulan sampah deterministik (dikonfirmasi).

class Users : IDisposable
    {
        ~Users()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
            // This method will remove current object from garbage collector's queue 
            // and stop calling finilize method twice 
        }    

        public void Dispose(bool disposer)
        {
            if (disposer)
            {
                // dispose the managed objects
            }
            // dispose the unmanaged objects
        }
    }

Saat membuat dan menggunakan kelas Pengguna, gunakan blok "menggunakan" untuk menghindari memanggil metode buang secara eksplisit:

using (Users _user = new Users())
            {
                // do user related work
            }

akhir menggunakan blok yang dibuat objek Pengguna akan dibuang dengan cara implisit metode buang.


2

Saya melihat banyak contoh pola Buang Microsoft yang benar-benar anti-pola. Seperti banyak yang telah menunjukkan kode dalam pertanyaan tidak memerlukan IDisposable sama sekali. Tetapi jika Anda ingin menerapkannya di mana, jangan gunakan pola Microsoft. Jawaban yang lebih baik akan mengikuti saran dalam artikel ini:

https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About

Satu-satunya hal lain yang mungkin akan membantu adalah menekan peringatan analisis kode itu ... https://docs.microsoft.com/en-us/visualstudio/code-quality/in-source-suppression-overview?view=vs- 2017

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.