ID'yi doğru bir şekilde uygulamak


145

Sınıflarımda IDisposable'ı aşağıdaki gibi uyguluyorum:

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;
    }
}

VS2012'de, Kod Analizim ID'yi tek kullanımlık olarak doğru bir şekilde uyguladığını söylüyor, ancak burada yanlış yaptığımdan emin değilim.
Tam metin aşağıdaki gibidir:

CA1063 ID'yi doğru bir şekilde uygulayın 'Kullanıcı'da geçersiz kılınabilen bir Dispose (bool) uygulaması sağlayın veya türü mühürlü olarak işaretleyin. Dispose (yanlış) çağrısı (false) yalnızca yerel kaynakları temizlemelidir. Dispose çağrısı (true) hem yönetilen hem de yerel kaynakları temizlemelidir. stman Kullanıcı.cs 10

Referans için: CA1063: Uygulama kimliği doğru bir şekilde atılabilir

Bu sayfayı okudum, ama korkarım burada ne yapılması gerektiğini gerçekten anlamıyorum.

Herhangi bir kimse sorunun ne olduğunu ve / veya IDisposable nasıl uygulanması gerektiğini daha lamens terimleri ile açıklayabilir, bu gerçekten yardımcı olacaktır!


1
İçindeki tüm kod bu Disposemu?
Claudio Redi

42
Sınıfınızın herhangi bir üyesinde Dispose () yöntemini çağırmak için Dispose () yönteminizi uygulamalısınız. Bu üyelerin hiçbirinde bir üye yok. Bu nedenle gereken değil IDisposable uygulamak. Özellik değerlerini sıfırlamak anlamsızdır.
Hans Passant

13
Sadece uygulamak gerekir IDispoablebu (sarılır yönetilmeyen kaynakları içerir (bertaraf etmek yönetilmeyen kaynakları varsa SqlConnection, FileStreamSen ve yok vb.) Olmamalıdır uygulamak IDisposablesadece kaynak yönetilen varsa böyle burada da. Bu IMO olduğu, kod analizi ile ilgili önemli bir sorun Aptalca küçük kuralları kontrol etmede çok iyi, ancak kavramsal hataları kontrol etmede iyi değil .
jason

51
Bazı insanlar, bir kavramı açıkça yanlış anlayan bir kişiye yardım etmeye çalışmaktan ziyade bu sorunun kapalı olduğunu ve bu sorunun kapalı olduğunu görmeyi çok üzüyorlardı. Ne utanç.
Ortund

2
Bu yüzden aşağıya düşürmeyin, oy vermeyin, gönderiyi sıfırda bırakın ve soruyu yararlı bir işaretçi ile kapatın.
tjmoore

Yanıtlar:


113

Gönderdiğiniz kodda atmanız gereken hiçbir şey görmeme rağmen bu doğru uygulama olurdu. Sadece şu durumlarda uygulamanız gerekir IDisposable:

  1. Yönetilmeyen kaynaklarınız var
  2. Tek kullanımlık olan şeylerin referanslarına tutunuyorsunuz.

Gönderdiğiniz koddaki hiçbir şeyin atılması gerekmez.

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
C # 'da yazmaya başladığım using(){ }zaman mümkün olan en iyi şekilde yararlanmanın en iyisi olduğu söylendi , ancak bunu yapmak için IDisposable'ı uygulamanız gerekiyor, bu yüzden genel olarak, bir sınıfa, usp. sınıfa yalnızca bir veya iki işlevde ihtiyacım varsa
Ortund

62
@Ortund Yanlış anladınız. Sınıf IDisposable uyguladığında bir usingblok kullanmak en iyisidir . Tek kullanımlık olması için bir sınıfa ihtiyacınız yoksa, onu uygulamayın. Hiçbir amaca hizmet etmez.
Daniel Mann

5
@DanielMann Bir usingbloğun anlambilimi, IDisposableyalnızca arayüzün ötesinde çekici olma eğilimindedir . IDisposableSadece kapsam belirleme amacıyla birkaç istismardan daha fazlası olduğunu düşünüyorum .
Thomas

1
Nasıl yönetilecek herhangi bir kaynağınız yoksa serbest bırakılacak bir yan not olarak, Çöp toplama işlemi sırasında (Dispose henüz çağrılmadıysa) GC'nin Finalizer'ı çağırmasına izin verecek olan Dispose çağırma (false) içermeli ve düzgün bir şekilde ücretsiz yönetilmemelidir kaynaklar.
mariozski

4
Uygulamanızda bir sonlandırıcı olmadan arama GC.SuppressFinalize(this);anlamsızdır. @Mariozski belirttiği gibi bir sonlandırıcı yardımcı olacağını sağlamak olduğunu Disposesınıf içinde kullanılan değilse hiç denir usingblokta.
Haymo Kutschbach

57

Her şeyden önce, strings ve ints "temizlemeniz" gerekmez - çöp toplayıcı tarafından otomatik olarak halledilecektir. Temizlenmesi gereken tek şey Disposeyönetilmeyen kaynaklar veya uygulanan yönetilen kaynaklar IDisposable.

Bununla birlikte, bunun sadece bir öğrenme uygulaması olduğunu varsayarsak, uygulamanın önerilen yolu, IDisposablekaynakların iki kez atılmamasını sağlamak için bir "güvenlik mandalı" eklemektir:

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, temizleme kodunun yalnızca bir kez yürütüldüğünden emin olmak için bir bayrak kullanmak, özellikleri null olarak veya herhangi bir şekilde ayarlamaktan çok daha iyidir (özellikle bu readonlyanlambilime müdahale ettiği için )
Thomas

Kullanıcı kodunu kullanmak için +1 (otomatik olarak temizlenecek olsa bile) oraya ne olduğunu netleştirmek için. Ayrıca, tuzlu bir denizci olmamanız ve burada diğerleri gibi öğrenirken küçük bir hata yapmak için onu dövmesi nedeniyle.
Murphybro2

42

Aşağıdaki örnek, IDisposablearabirimi uygulamak için genel en iyi uygulamayı gösterir . Referans

Yalnızca sınıfınızda yönetilmeyen kaynaklarınız varsa bir yıkıcıya (sonlandırıcı) ihtiyacınız olduğunu unutmayın. Bir yıkıcı eklerseniz, Atma işleminde Sonlandırmayı bastırmanız gerekir , aksi takdirde nesnelerinizin iki çöp döngüsü için bellekte kalmasına neden olur (Not: Sonlandırmanın nasıl çalıştığını okuyun ). Aşağıdaki örnek yukarıda açıklanmıştır.

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

IDisposableÇöp Toplayıcı tarafından otomatik olarak temizlenmeyecek yönetilmeyen kaynakları temizlemeniz için bir araç sağlamak için mevcuttur .

"Temizlediğiniz" tüm kaynaklar yönetilen kaynaklardır ve bu nedenle Disposeyönteminiz hiçbir şey başaramaz. Sınıfınız hiç uygulamamalı IDisposable. Çöp Toplayıcı tüm bu alanlarla tek başına ilgilenecektir.


1
Bunu kabul edin - aslında gerekli olmadığında her şeyi elden çıkarma fikri vardır. Atma işlemi yalnızca temizlenmesi için yönetilmeyen kaynaklarınız varsa kullanılmalıdır!
Chandramouleswaran Ravichandra

4
Kesinlikle doğru değil, Dispose yöntemi aynı zamanda "IDisposable'ı uygulayan yönetilen kaynakları"
atmanıza

@MattWilko Bu , yönetilmeyen kaynakların atılmasının dolaylı bir yolunu yapar , çünkü başka bir kaynağın yönetilmeyen bir kaynağı atmasına izin verir. Burada başka bir yönetilen kaynak aracılığıyla yönetilmeyen bir kaynağa dolaylı bir referans bile yoktur .
15'te

@MattWilko imha otomatik herhangi IDesposable uygulanan Yönetilen kaynakta denilen edecek
panky sharma

@pankysharma Hayır, olmayacak. Bu gereken elle aradı. Bütün mesele bu. Sen olamaz sen sadece insanlar biliyorum, otomatik olarak çağrılır varsaymak gerekiyordu elle aramak, ama insanlar yapmak hataları yapmak ve unutmak.
Servy

14

Tek Kullanımlık Kalıbı şu şekilde kullanmanız gerekir :

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
yıkıcıda GC.SuppressFinalize (bu) çağrısı yapmak akıllıca olmaz mıydı? Aksi takdirde, nesnenin kendisi bir sonraki
GC'de

2
@ dotnetguy: gc çalıştırıldığında nesne yıkıcı çağrılır. Yani iki kere arama yapmak mümkün değil. Buraya bakın: msdn.microsoft.com/en-us/library/ms244737.aspx
schoetbi

1
Peki, şimdi herhangi bir kaynak plakası parçasını "kalıp" olarak mı adlandırıyoruz?
Chel

4
@rdhs Hayır değiliz. MSDN devletler IS Burada bir örüntü "imha Desen" - msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx yüzden önce Google biraz belki aşağı oylama?
Belogix

2
Ne Microsoft ne de yayınınız, desenin neden böyle görünmesi gerektiğini açıkça belirtmiyor. Genellikle, kaynatma plakası bile değildir, sadece gereksizdir SafeHandle(ve alt tipler). Yönetilen kaynaklar söz konusu olduğunda, uygun bertarafın uygulanması çok daha basit hale gelir; kodu, void Dispose()yöntemin basit bir uygulamasına getirebilirsiniz .
BatteryBackupUnit

10

Sınıf yönetilmeyen herhangi bir kaynak (dosya, veritabanı bağlantısı vb.) Edinmediği için Usersınıfınızı yapmanıza gerek yoktur . Genellikle, sınıfları en az bir alanı veya / ve özelliği varmış gibi işaretleriz . Uygularken , Microsoft tipik şemasına göre daha iyi koyun:IDisposableIDisposableIDisposableIDisposable

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);
  } 
}

Budur genelde durum. Ancak öte yandan, using yapısı, C ++ akıllı işaretleyicilerine benzer bir şey yazma olasılığını, yani bir kullanım bloğundan nasıl çıkılırsa çıkılsın, önceki durumu geri yükleyecek bir nesne yazma olasılığını açar. Bunu yapmanın tek yolu böyle bir nesneyi ID imkansız kılmak. Bu kadar marjinal bir kullanım durumunda derleyicinin uyarısını görmezden gelebilirim.
Papa Smurf

3

Idisposable, deterministik (onaylanmış) bir çöp toplama istediğinizde uygulanır.

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
        }
    }

Users sınıfını oluştururken ve kullanırken açıkça dispose yöntemini çağırmaktan kaçınmak için "using" bloğunu kullanın:

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

blok kullanılarak oluşturulan Users nesnesinin sonu, dispose yönteminin örtülü olarak çağrılmasıyla bertaraf edilecektir.


2

Gerçekten bir anti-desen olan Microsoft Dispose deseninin birçok örneğini görüyorum. Birçoğunun belirttiği gibi, sorudaki kod hiç IDisposable gerektirmez. Ama nereye uygulayacaksanız lütfen Microsoft modelini kullanmayın. Daha iyi cevap bu makaledeki önerileri takip etmek olacaktır:

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

Yararlı olabilecek tek şey kod analizi uyarısını bastırmaktır ... https://docs.microsoft.com/en-us/visualstudio/code-quality/in-source-suppression-overview?view=vs- 2017

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.