2017-06-23 8 views
6

कल एक माता पिता में पारित हो जाता है जब क्या जिसके परिणामस्वरूप व्यवहार है, हमारे कोडबेस पर दृश्य स्टूडियो कोड विश्लेषण के चलने के बाद, निम्न कोड एक मुद्दे के रूप में डाला गया था थाएक IDisposable IDisposable

चेतावनी CA2202 वस्तु 'stringReader' एक बार विधि '(विधि नाम)' की तुलना में अधिक निपटाया जा सकता है। सिस्टम उत्पन्न करने से बचने के लिए। ऑब्जेक्ट डिस्प्ले अपवाद जिसे आप कॉल नहीं करना चाहिए किसी ऑब्जेक्ट पर से अधिक समय का निपटान करें।

खोज ढेर अतिप्रवाह के बाद, मैं सामान्य समझ पर आए हैं कि अगर मैं IDisposable सदस्यों से युक्त एक कस्टम वर्ग बनाने के लिए थे, यह IDisposable खुद को लागू करना चाहिए, और सदस्य की dispose() विधि कॉल।

मेरे दो सवाल

  • सभी मामलों में जहां वस्तु एक्स निर्माण के दौरान एक पैरामीटर के रूप में एक IDisposable वस्तु वाई के लिए एक संदर्भ लेता में हैं, यह मान लेना सही है कि वस्तु X Y की और से स्वामित्व ले जाएगा उस समय के बाद, X.dispose() बुला होगा हमेशाY.dispose()
  • बुला में परिणाम यह कोड और अपवादों चेतावनी संदेश में वर्णित के रूप के एक पुराने टुकड़ा सूचित किया गया कभी नहीं किया है (मेरी जानकारी के अनुसार) है। यदि उपर्युक्त बिंदु माना जाता है, तो डबल using ब्लॉक क्यों stringReader.dispose() को दो बार कॉल करने और अपवाद फेंकने का परिणाम नहीं देता है?
+0

आपको क्या उम्मीद करनी चाहिए कि संलग्न प्रकार के कार्यान्वयनकर्ता ने आपके द्वारा किए गए डिस्पोजेबल के संबंध में * उसका व्यवहार * दस्तावेज किया है। –

उत्तर

2

ग्रहण करने के लिए यह सही है कि वस्तु X Y की और उस समय के बाद से स्वामित्व) ले जाएगा, X.dispose (बुला हमेशा Y.dispose() कॉल में परिणाम होगा

नहीं, यह कभी ग्रहण करने के लिए कभी नहीं बचा है। आइए इस विशिष्ट मामले की जांच करें: XmlReader.Create(Stream)

संदर्भ स्रोत में काफी कुछ कोड जाने के बाद, मैंने पाया है कि Dispose विधि Close विधि को कॉल करती है। यह काफी स्पष्ट है। तब this piece of code नोटिस:

public override void Close() { 
    Close(closeInput); 
} 

इसलिए चाहे समर्थन धारा बंद कर दिया और निपटारा किया जाएगा आप XmlReaderSettings.CloseInput सेटिंग के माध्यम से सेट कर सकते हैं जो की स्थापना closeInput के मूल्य पर निर्भर करता है।

तो यहां का जवाब एक निश्चित संख्या है: आप सुनिश्चित नहीं कर सकते कि यह निपटाया गया है। आपको हमेशा यह सुनिश्चित करना चाहिए कि यह स्वयं है।

1

सभी मामलों में जहां ऑब्जेक्ट एक्स निर्माण के दौरान पैरामीटर के रूप में एक आईडीस्पोजेबल ऑब्जेक्ट वाई का संदर्भ लेता है, क्या यह मानना ​​सही है कि ऑब्जेक्ट एक्स वाई का स्वामित्व लेगा और उस बिंदु से XDispose को कॉल करेगा() हमेशा Y.dispose()

मुझे लगता है कि नहीं और मैं समझाने की कोशिश करूंगा क्यों।

एक पैटर्न IDisposablePattern कहा जाता है जो कि तरह दिखता है:

public class SimpleClass2: IDisposable 
{ 
    // managed resources 
    private SqlConnection _connection; 
    private bool _disposed; 

    // unmanaged resources 
    private IntPtr _unmanagedResources; 

    // simple method for the demo 
    public string GetDate() 
    { 
     // One good practice that .NET Framework implies is that when object is being disposed 
     // trying to work with its resources should throw ObjectDisposedException so.. 
     if(_disposed) { throw new ObjectDisposedException(this.GetType().Name);} 

     if (_connection == null) 
     { 
      _connection = new SqlConnection("Server=.\\SQLEXPRESS;Database=master;Integrated Security=SSPI;App=IDisposablePattern"); 
      _connection.Open(); 
     } 
     // allocation of unmanaged resources for the sake of demo. 
     if (_unmanagedResources == IntPtr.Zero) 
     { 
      _unmanagedResources = Marshal.AllocHGlobal(100 * 1024 * 1024); 
     } 

     using (var command = _connection.CreateCommand()) 
     { 
      command.CommandText = "SELECT getdate()"; 
      return command.ExecuteScalar().ToString(); 
     } 
    } 


    public void Dispose() 
    { 
     // Here in original Dispose method we call protected method with parameter true, 
     // saying that this object is being disposed. 
     this.Dispose(true); 

     // Then we "tell" garbage collector to suppress finalizer for this object because we are releasing 
     // its memory and doesnt need to be finalized. Calling finalizer(destructor) of a given type is expensive 
     // and tweaks like this help us improve performance of the application. 

     // This is only when your class doesnt have unmanaged resources!!! 
     // Since this is just made to be a demo I will leave it there, but this contradicts with our defined finalizer. 
     GC.SuppressFinalize(this); 
    } 

    // Following the best practices we should create another method in the class 
    // with parameter saying wether or not the object is being disposed. 
    // Its really important that this method DOES NOT throw exceptions thus allowing to be called multiple times 
    protected virtual void Dispose(bool disposing) 
    { 
     // another thing we may add is flag that tells us if object is disposed already 
     // and use it here 
     if (_disposed) { return; } 
     // Thus Dispose method CAN NOT release UNMANAGED resources such as IntPtr structure, 
     // flag is also helping us know whether we are disposing managed or unmanaged resources 
     if (disposing) 
     { 
      if (_connection != null) 
      { 
       _connection.Dispose(); 
       _connection = null; 
      } 
      _disposed = true; 
     } 
     // Why do we need to do that? 
     // If consumer of this class forgets to call its Dispose method (simply by not using the object in "using" statement 
     // Nevertheless garbage collector will fire eventually and it will invoke Dispose method whats the problem with that is if we didn't 
     // have the following code unmanaged resources wouldnt be disposed , because as we know GC cant release unmanaged code. 
     // So thats why we need destructor(finalizer). 
     if (_unmanagedResources != IntPtr.Zero) 
     { 
      Marshal.FreeHGlobal(_unmanagedResources); 
      _unmanagedResources = IntPtr.Zero;; 
     } 
     // call base Dispose(flag) method if we are using hierarchy. 
    } 

    ~DatabaseStateImpr() 
    { 
     // At this point GC called our finalizer method , meaning 
     // that we don't know what state our managed resources are (collected or not) because 
     // our consumer may not used our object properly(not in using statement) so thats why 
     // we skip unmanaged resources as they may have been finalized themselves and we cant guarantee that we can 
     // access them - Remember? No exceptions in Dispose methods. 
     Dispose(false); 
    } 
} 
2
    :

    public class SimpleClass : IDisposable 
    { 
        // managed resources SqlConnection implements IDisposable as well. 
        private SqlConnection _connection; 
        private bool _disposed; 
    
        // implementing IDisposable 
        public void Dispose() 
        { 
         // Here in original Dispose method we call protected method with parameter true, 
         // saying that this object is being disposed. 
         this.Dispose(true); 
    
         // Then we "tell" garbage collector to suppress finalizer for this object because we are releasing 
         // its memory and doesnt need to be finalized. Calling finalizer(destructor) of a given type is expensive 
         // and tweaks like this help us improve performance of the application. 
         GC.SuppressFinalize(this); 
        } 
    
        // Following the best practices we should create another method in the class 
        // with parameter saying whether or not the object is being disposed. 
        // Its really important that this method DOES NOT throw exceptions thus allowing to be called multiple times 
        protected virtual void Dispose(bool disposing) 
        { 
         // another thing we may add is flag that tells us if object is disposed already 
         // and use it here 
         if (_disposed) { return; } 
         if (_connection != null) 
         { 
          _connection.Dispose(); 
          _connection = null; 
         } 
         _disposed = true; 
    
         // call base Dispose(flag) method if we are using hierarchy. 
        } 
    } 
    

    ध्यान दें कि यह नए स्तर पर बढ़ाया जा सकता है जब अपने वर्ग इस तरह अप्रबंधित संसाधनों का उपयोग करता

  • नहीं, आप यह नहीं मान सकते कि एक और ऑब्जेक्ट स्वयं को निपटाने के दौरान Dispose() पर कॉल करेगा। एक संदर्भ के रूप में डिस्पोजेबल ऑब्जेक्ट वाला ऑब्जेक्ट डिस्पोजेबल संसाधन का उपयोग भी नहीं कर सकता है।
  • यह चेतावनी कुछ अजीब होने के लिए जाना जाता है। चेतावनी के बारे में कुछ शिकायतों को देखने के लिए here देखें। आपको अपनी कक्षा को डिज़ाइन करना चाहिए ताकि Dispose() को एक से अधिक बार कॉल करना सुरक्षित हो।

वैसे, MSDN का कहना है:

एक विधि कार्यान्वयन कोड रास्तों कि इस तरह के एक बंद() कुछ प्रकार पर विधि के रूप में IDisposable.Dispose या एक निपटान बराबर करने के लिए कई कॉल, का कारण बन सकता है, एक ही वस्तु पर।

तो Close() विधि कॉल का पथ इस चेतावनी को भी उत्पन्न कर सकता है, यही कारण है कि आप अपने मामले में यह चेतावनी देखते हैं।

संबंधित मुद्दे