C # में फाइनल / डिस्पोजल विधि का उपयोग


381

सी # 2008

मैं इस पर कुछ समय से काम कर रहा हूं, और मैं अभी भी कोड में अंतिम रूप देने और निपटान के तरीकों के बारे में उलझन में हूं। मेरे सवाल नीचे हैं:

  1. मुझे पता है कि मानव रहित संसाधनों का निपटान करते हुए हमें केवल एक अंतिम रूप देने की आवश्यकता है। हालांकि, यदि प्रबंधित संसाधन हैं जो अप्रबंधित संसाधनों को कॉल करते हैं, तो क्या अभी भी इसे अंतिम रूप देने की आवश्यकता होगी?

  2. हालाँकि, अगर मैं एक ऐसे वर्ग का विकास करता हूँ जो प्रत्यक्ष या अप्रत्यक्ष रूप से किसी भी अप्रबंधित संसाधन का उपयोग नहीं करता है, तो क्या मुझे IDisposableउस वर्ग के ग्राहकों को 'कथन का उपयोग करने' की अनुमति देने के लिए लागू करना चाहिए ?

    क्या केवल आपके कथन का उपयोग करने के लिए अपने वर्ग के ग्राहकों को सक्षम करने के लिए आईडीसोपायरी को लागू करना संभव होगा?

    using(myClass objClass = new myClass())
    {
        // Do stuff here
    }
  3. मैंने फाइनल / डिस्पोजल उपयोग को प्रदर्शित करने के लिए नीचे यह सरल कोड विकसित किया है:

    public class NoGateway : IDisposable
    {
        private WebClient wc = null;
    
        public NoGateway()
        {
            wc = new WebClient();
            wc.DownloadStringCompleted += wc_DownloadStringCompleted;
        }
    
    
        // Start the Async call to find if NoGateway is true or false
        public void NoGatewayStatus()
        {
            // Start the Async's download
                // Do other work here
            wc.DownloadStringAsync(new Uri(www.xxxx.xxx));
        }
    
        private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            // Do work here
        }
    
        // Dispose of the NoGateway object
        public void Dispose()
        {
            wc.DownloadStringCompleted -= wc_DownloadStringCompleted;
            wc.Dispose();
            GC.SuppressFinalize(this);
        }
    }

स्रोत कोड के बारे में प्रश्न:

  1. यहाँ मैंने अंतिम रूप नहीं जोड़ा है, और सामान्य रूप से अंतिम रूप से GC को बुलाया जाएगा, और अंतिम रूप से Dispose को कॉल करेगा। जैसा कि मेरे पास अंतिम रूप नहीं है, मैं निपटान विधि कब कहूं? क्या यह उस वर्ग का ग्राहक है जिसे इसे कॉल करना है?

    इसलिए उदाहरण में मेरी कक्षा को NoGateway कहा जाता है और ग्राहक इस तरह वर्ग का उपयोग और निपटान कर सकता है:

    using(NoGateway objNoGateway = new NoGateway())
    {
        // Do stuff here   
    }

    क्या डिस्पोज़ विधि को स्वचालित रूप से बुलाया जाएगा जब निष्पादन ब्लॉक के अंत तक पहुंच जाता है, या क्लाइंट को मैन्युअल रूप से डिस्पोज़ विधि को कॉल करना पड़ता है? अर्थात

    NoGateway objNoGateway = new NoGateway();
    // Do stuff with object
    objNoGateway.Dispose(); // finished with it
  2. मैं WebClientअपनी NoGatewayकक्षा में कक्षा का उपयोग कर रहा हूं । क्योंकि इंटरफ़ेस को WebClientलागू करता IDisposableहै, इसका मतलब यह है कि WebClientअप्रत्यक्ष रूप से अप्रबंधित संसाधनों का उपयोग करता है? क्या इसका पालन करने के लिए एक कठिन और तेज़ नियम है? मुझे कैसे पता चलेगा कि एक वर्ग अप्रबंधित संसाधनों का उपयोग करता है?


1
क्या यह जटिल डिज़ाइन पैटर्न वास्तव में इस संसाधन रिले समस्या को हल करने के लिए आवश्यक है?
ज़िंग

जवाबों:


422

सुझाए गए आईडीसिसोप्लिक पैटर्न यहां हैं । जब एक कक्षा का उपयोग करने वाले आईडीआईएसओफ़ेक्टिव का प्रोग्रामिंग करते हैं, तो आमतौर पर आपको दो पैटर्न का उपयोग करना चाहिए:

जब एक सील किए गए वर्ग को लागू करना, जो अप्रबंधित संसाधनों का उपयोग नहीं करता है, तो आप बस सामान्य इंटरफ़ेस कार्यान्वयन के साथ डिस्पोज़ विधि लागू करते हैं:

public sealed class A : IDisposable
{
    public void Dispose()
    {
        // get rid of managed resources, call Dispose on member variables...
    }
}

एक अनसेल्ड क्लास लागू करते समय, इसे इस तरह करें:

public class B : IDisposable
{    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }   
        // get rid of unmanaged resources
    }

    // only if you use unmanaged resources directly in B
    //~B()
    //{
    //    Dispose(false);
    //}
}

ध्यान दें कि मैंने अंतिम रूप से घोषित नहीं किया है B; यदि आपको निपटान करने के लिए वास्तविक अप्रबंधित संसाधन हैं, तो आपको केवल एक अंतिम रूप लागू करना चाहिए। सीएलआर अंतिम रूप देने योग्य वस्तुओं के साथ गैर-अंतिम करने योग्य वस्तुओं के लिए अलग-अलग व्यवहार करता है, भले ही SuppressFinalizeइसे कहा जाता हो।

इसलिए, आपको तब तक एक अंतिम रूप देने की घोषणा नहीं करनी चाहिए जब तक आपको नहीं करना है, लेकिन आप अपनी कक्षा के उत्तराधिकारियों को कॉल करने के लिए एक हुक देते हैं Disposeऔर यदि वे सीधे अप्रबंधित संसाधनों का उपयोग करते हैं, तो खुद को अंतिम रूप से लागू करने के लिए:

public class C : B
{
    private IntPtr m_Handle;

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }
        ReleaseHandle(m_Handle);

        base.Dispose(disposing);
    }

    ~C() {
        Dispose(false);
    }
}

यदि आप सीधे अप्रबंधित संसाधनों का उपयोग नहीं कर रहे हैं ( SafeHandleऔर दोस्तों की गिनती नहीं है, जैसा कि वे अपने स्वयं के फाइनल को घोषित करते हैं), तो एक फाइनल को लागू न करें, क्योंकि जीसी अंतिम रूप से अलग-अलग वर्गों से संबंधित है, भले ही आप बाद में फाइनल को दबा दें। यह भी ध्यान दें कि, भले ही Bएक अंतिम रूप नहीं है, फिर भी यह SuppressFinalizeकिसी भी उपवर्ग के साथ सही ढंग से निपटने के लिए कहता है जो एक अंतिम कार्यान्वयन को लागू करते हैं।

जब कोई वर्ग आईडीसोफ़रेबल इंटरफ़ेस लागू करता है, तो इसका मतलब है कि कहीं न कहीं कुछ अप्रबंधित संसाधन हैं जिन्हें तब उपयोग किया जाना चाहिए जब आपने कक्षा का उपयोग करना समाप्त कर लिया हो। वास्तविक संसाधनों को कक्षाओं के भीतर समझाया जाता है; आपको उन्हें स्पष्ट रूप से हटाने की आवश्यकता नहीं है। बस Dispose()एक using(...) {}वसीयत में कक्षा को कॉल करने या लपेटने से यह सुनिश्चित होगा कि किसी भी अप्रबंधित संसाधन को आवश्यक रूप से छुटकारा मिल जाएगा।


26
मैं थियोकोप से सहमत हूं। ध्यान दें कि आपको अंतिम रूप देने की आवश्यकता नहीं है यदि आप केवल प्रबंधित संसाधनों के साथ काम कर रहे हैं (वास्तव में, आपको अपने अंतिम रूप से "इस" के अलावा) से प्रबंधित वस्तुओं तक पहुंचने का प्रयास नहीं करना चाहिए, क्योंकि इसमें कोई गारंटी वाला आदेश नहीं है जीसी वस्तुओं को साफ करेगा। इसके अलावा, यदि आप .Net 2.0 या बेहतर का उपयोग कर रहे हैं, तो आप अनवांटेड हैंडल को सुरक्षित करने के लिए SafeHandles का उपयोग कर सकते हैं (और चाहिए)। Safehandles आपकी प्रबंधित कक्षाओं के लिए फाइनल लिखने की आवश्यकता को बहुत कम कर देता है। blog .msdn com / bclteam / संग्रह / 2005/03/16 / 396900.aspx
JMarsch

5
मुझे लगता है कि MessageBox.Show ("त्रुटि," + GetType ()। नाम + "नहीं निपटारा") में कॉल करना बेहतर है, क्योंकि अंतिम रूप से डिस्पोजेबल ऑब्जेक्ट को हमेशा अलग किया जाना चाहिए, और यदि आप ऐसा करने में विफल रहते हैं। इस तथ्य से जल्द से जल्द सावधान रहना चाहिए।
एरिक्कलेन

95
@erikkallen क्या यह मजाक है? :)
एलेक्स नोरक्लिफ

2
सीएलआर में अतिरिक्त कंप्यूटिंग प्रयास की आवश्यकता है ताकि सक्रिय फाइनल के साथ कक्षाओं का ट्रैक रखा जा सके। - फाइनल को लागू करने से ऐसा होता है। GC.SuppressFinalize को कॉल करने का अर्थ है कि फाइनलटाइम को रनटाइम द्वारा नहीं बुलाया जाना चाहिए। यह अभी भी परवाह किए बिना Gen2 चला जाता है। यदि आप प्रबंधित संसाधनों के साथ काम नहीं कर रहे हैं, तो अंतिम रूप न जोड़ें। सीलबंद या बिना वर्ग के संशोधक उस बिंदु के लिए अप्रासंगिक हैं।
रिच मेल्टन

3
@ रिच: प्रशस्ति पत्र? जरूरी नहीं कि वह बुरी चीज हो; यदि आप लागू कर रहे हैं IDisposable, तो संभावना है कि यह वैसे भी थोड़ी देर के लिए चारों ओर लटका रहेगा। आप CLR को Gen0 -> Gen1 -> Gen2
thecoop

123

लागू करने के लिए आधिकारिक पैटर्न IDisposableसमझना मुश्किल है। मेरा मानना ​​है कि यह बेहतर है :

public class BetterDisposableClass : IDisposable {

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

  protected virtual void CleanUpManagedResources() { 
    // ...
  }
  protected virtual void CleanUpNativeResources() {
    // ...
  }

  ~BetterDisposableClass() {
    CleanUpNativeResources();
  }

}

एक और भी बेहतर समाधान एक नियम है करने के लिए आपको लगता है कि है हमेशा किसी भी अप्रबंधित संसाधन के लिए एक आवरण वर्ग बनाने के लिए है कि आप संभाल करने की जरूरत है:

public class NativeDisposable : IDisposable {

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

  protected virtual void CleanUpNativeResource() {
    // ...
  }

  ~NativeDisposable() {
    CleanUpNativeResource();
  }

}

इसके SafeHandleऔर इसके व्युत्पन्न के साथ, ये कक्षाएं बहुत दुर्लभ होनी चाहिए ।

डिस्पोजेबल वर्गों के लिए परिणाम जो अप्रबंधित संसाधनों के साथ सीधे व्यवहार नहीं करते हैं, यहां तक ​​कि विरासत की उपस्थिति में, शक्तिशाली है: उन्हें अब अप्रबंधित संसाधनों से चिंतित होने की आवश्यकता नहीं है । वे लागू करने और समझने में सरल होंगे :

public class ManagedDisposable : IDisposable {

  public virtual void Dispose() {
    // dispose of managed resources
  }

}

@ केश: धन्यवाद! मुझे यह बहुत पसंद है :-) यहाँ पर इसका अनुसरण है
जोर्डो

4
हालांकि एक बात जो मैं नोट करना चाहता हूं, वह यह नहीं है कि दूसरी बार बुलाया जाए।
हुसैनुअस्लू

5
@HuseyinUslu: यह सिर्फ पैटर्न का सार है। आप निश्चित रूप से एक disposedध्वज जोड़ सकते हैं और तदनुसार जांच कर सकते हैं ।
जोर्डो

2
@ डिडीबस: यह एक disposedध्वज जोड़ने का एक साधारण मामला है , इसे निपटाने से पहले जांचें और निपटाने के बाद इसे सेट करें। विचार के लिए यहां देखें । आपको कक्षा के किसी भी तरीके से पहले ध्वज की जांच करनी चाहिए। समझ में आता है? क्या यह जटिल है?
जोर्डो

1
+1 के लिए "एक और भी बेहतर समाधान एक नियम है कि आपको हमेशा किसी भी अप्रबंधित संसाधन के लिए एक आवरण वर्ग बनाना होगा जिसे आपको संभालना होगा" । मैं वीएलसी के लिए एक ऐडऑन में इस पर ठोकर खाई और मैं तब से इसका उपयोग कर रहा हूं। इतने सिर दर्द
फ्रांज बी।

37

ध्यान दें कि किसी भी आईडीसोपायरी कार्यान्वयन को निम्न पैटर्न (आईएमएचओ) का पालन करना चाहिए। मैंने कई उत्कृष्ट .NET "देवताओं" से जानकारी के आधार पर इस पैटर्न को विकसित किया। .NET फ्रेमवर्क डिज़ाइन दिशानिर्देश (ध्यान दें कि MSDN किसी कारण से इसका पालन नहीं करता है!)। .NET फ्रेमवर्क डिज़ाइन दिशा-निर्देश क्रिज़ीसॉफ्ट कॉवेलिना (उस समय सीएलआर आर्किटेक्ट) और ब्रैड एब्राम (उस समय सीएलआर प्रोग्राम मैनेजर का मानना ​​है) और बिल वैगनर ([प्रभावी सी #]] और [अधिक प्रभावी # #] द्वारा लिखे गए थे Amazon.com पर इनकी तलाश करें:

ध्यान दें कि आपको तब तक एक फ़ाइनलीज़र लागू करना चाहिए जब तक कि आपकी कक्षा में सीधे (विरासत में नहीं) संयुक्त संसाधन न हों। एक बार जब आप एक कक्षा में एक फाइनल को लागू करते हैं, भले ही इसे कभी नहीं कहा जाता है, यह एक अतिरिक्त संग्रह के लिए रहने की गारंटी है। यह स्वचालित रूप से अंतिम रूप कतार (जो एक ही धागे पर चलता है) पर रखा गया है। इसके अलावा, एक बहुत ही महत्वपूर्ण नोट ... सभी कोड को एक अंतिम रूप में निष्पादित किया जाना चाहिए (क्या आपको एक को लागू करने की आवश्यकता है) आवश्यक रूप से थ्रेड-सुरक्षित और अपवाद-सुरक्षित होना चाहिए! BAD चीजें अन्यथा हो जाएंगी ... (यानी अनिर्धारित व्यवहार और अपवाद के मामले में, एक घातक अपरिवर्तनीय दुर्घटना)।

पैटर्न मैंने एक साथ रखा है (और एक कोड स्निपेट लिखा है) निम्नानुसार है:

#region IDisposable implementation

//TODO remember to make this class inherit from IDisposable -> $className$ : IDisposable

// Default initialization for a bool is 'false'
private bool IsDisposed { get; set; }

/// <summary>
/// Implementation of Dispose according to .NET Framework Design Guidelines.
/// </summary>
/// <remarks>Do not make this method virtual.
/// A derived class should not be able to override this method.
/// </remarks>
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.

    // Always use SuppressFinalize() in case a subclass
    // of this type implements a finalizer.
    GC.SuppressFinalize( this );
}

/// <summary>
/// Overloaded Implementation of Dispose.
/// </summary>
/// <param name="isDisposing"></param>
/// <remarks>
/// <para><list type="bulleted">Dispose(bool isDisposing) executes in two distinct scenarios.
/// <item>If <paramref name="isDisposing"/> equals true, the method has been called directly
/// or indirectly by a user's code. Managed and unmanaged resources
/// can be disposed.</item>
/// <item>If <paramref name="isDisposing"/> 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.</item></list></para>
/// </remarks>
protected virtual void Dispose( bool isDisposing )
{
    // TODO If you need thread safety, use a lock around these 
    // operations, as well as in your methods that use the resource.
    try
    {
        if( !this.IsDisposed )
        {
            if( isDisposing )
            {
                // TODO Release all managed resources here

                $end$
            }

            // TODO Release all unmanaged resources here



            // TODO explicitly set root references to null to expressly tell the GarbageCollector
            // that the resources have been disposed of and its ok to release the memory allocated for them.


        }
    }
    finally
    {
        // explicitly call the base class Dispose implementation
        base.Dispose( isDisposing );

        this.IsDisposed = true;
    }
}

//TODO Uncomment this code if this class will contain members which are UNmanaged
// 
///// <summary>Finalizer for $className$</summary>
///// <remarks>This finalizer will run only if the Dispose method does not get called.
///// It gives your base class the opportunity to finalize.
///// DO NOT provide finalizers in types derived from this class.
///// All code executed within a Finalizer MUST be thread-safe!</remarks>
//  ~$className$()
//  {
//     Dispose( false );
//  }
#endregion IDisposable implementation

यहां एक व्युत्पन्न वर्ग में आईडीसोपायरी को लागू करने के लिए कोड है। ध्यान दें कि आपको व्युत्पन्न वर्ग की परिभाषा में स्पष्ट रूप से आईडीसिसोपोलिट से विरासत की सूची की आवश्यकता नहीं है।

public DerivedClass : BaseClass, IDisposable (remove the IDisposable because it is inherited from BaseClass)


protected override void Dispose( bool isDisposing )
{
    try
    {
        if ( !this.IsDisposed )
        {
            if ( isDisposing )
            {
                // Release all managed resources here

            }
        }
    }
    finally
    {
        // explicitly call the base class Dispose implementation
        base.Dispose( isDisposing );
    }
}

मैंने इस कार्यान्वयन को अपने ब्लॉग पर पोस्ट किया है: डिस्पोज़ पैटर्न को ठीक से कैसे लागू किया जाए


क्या कोई भी व्युत्पन्न वर्ग (इस आधार वर्ग से प्राप्त) के लिए एक पैटर्न जोड़ सकता है
akjoshi

3
@akjoshi - मैंने एक व्युत्पन्न डिस्पोजेबल वर्ग के लिए कोड को शामिल करने के लिए उपरोक्त पैटर्न को अपडेट किया है। यह भी ध्यान दें, एक व्युत्पन्न वर्ग में एक अंतिम रूप से लागू करने के लिए ...
डेव ब्लैक

3
Microsoft डिस्पोज़ किए गए विधि के अंत में "डिस्पोज़्ड" फ्लैग को सेट करना पसंद करता है, लेकिन यह मुझे गलत लगता है। निरर्थक कॉल "विवाद" कुछ नहीं करने के लिए माना जाता है; जबकि कोई आमतौर पर उम्मीद नहीं करेगा कि डिस्पोज़ को पुन: प्राप्त करने के लिए कहा जाए, ऐसी चीजें हो सकती हैं यदि कोई एक ऐसी वस्तु को डिस्पोज़ करने की कोशिश कर रहा है, जो निर्माण या किसी अन्य ऑपरेशन के दौरान एक अपवाद द्वारा अमान्य स्थिति में छोड़ दी गई हो। मुझे लगता है कि गैर-आभासी आवरण फ़ंक्शन में Interlocked.Exchangeपूर्णांक IsDisposedध्वज का उपयोग करना सुरक्षित होगा।
सुपरकैट

@ डब्लबैक: क्या होगा यदि आपका आधार वर्ग अप्रबंधित संसाधनों का उपयोग नहीं करता है, लेकिन आपका व्युत्पन्न वर्ग करता है? क्या आपको फ़ाइनलाइज़र को व्युत्पन्न वर्ग में लागू करना है? और यदि हां, तो आपको कैसे पता चलेगा कि अगर आपके पास स्रोत तक पहुंच नहीं है तो बेस क्लास ने इसे पहले ही लागू नहीं किया है?
डिडियर ए।

@DaveBlack "मैंने कई उत्कृष्ट .NET" देवताओं "से जानकारी के आधार पर इस पैटर्न को विकसित किया है" यदि देवताओं में से एक जॉन स्कीट था तो मैं आपकी सलाह का पालन करूंगा।
एलिजाबेथ

23

मैं pm100 से सहमत हूं (और मुझे अपने पहले के पोस्ट में स्पष्ट रूप से यह कहना चाहिए था)।

जब तक आपको इसकी आवश्यकता न हो, तब तक आपको कभी भी किसी वर्ग में आईडीसोपायरी को लागू नहीं करना चाहिए। बहुत विशिष्ट होने के लिए, लगभग 5 बार ऐसा होता है जब आपको कभी भी आवश्यकता होगी / आईडीआईसपोजेबल लागू करना चाहिए:

  1. आपकी कक्षा में स्पष्ट रूप से शामिल हैं (अर्थात उत्तराधिकार के माध्यम से नहीं) कोई भी प्रबंधित संसाधन जो आईडीआईसोपायरी को लागू करते हैं और एक बार आपकी कक्षा का उपयोग नहीं किए जाने पर साफ हो जाना चाहिए। उदाहरण के लिए, यदि आपकी कक्षा में स्ट्रीम, DbCommand, DataTable, आदि का उदाहरण है।

  2. आपकी कक्षा में स्पष्ट रूप से कोई प्रबंधित संसाधन शामिल हैं जो एक बंद () विधि को लागू करते हैं - जैसे IDataReader, IDbConnection, इत्यादि ध्यान दें कि इनमें से कुछ वर्ग डिस्पोज़ () के साथ-साथ एक बंद () पद्धति से आईडीआईसॉपी लागू करते हैं।

  3. आपकी कक्षा में स्पष्ट रूप से एक अप्रबंधित संसाधन होता है - जैसे एक COM ऑब्जेक्ट, पॉइंटर्स (हाँ, आप प्रबंधित C # में पॉइंटर्स का उपयोग कर सकते हैं लेकिन उन्हें 'असुरक्षित' ब्लॉक आदि में घोषित किया जाना चाहिए। अप्रबंधित संसाधनों के मामले में, आपको भी सुनिश्चित करना चाहिए। RC.W पर System.Runtime.InteropServices.Marshal.ReleaseComObject () कॉल करें। भले ही RCW, सिद्धांत रूप में, एक प्रबंधित आवरण है, फिर भी कवर के तहत चल रही संदर्भ गिनती है।

  4. यदि आपकी कक्षा मजबूत संदर्भों का उपयोग करके घटनाओं की सदस्यता लेती है। आपको घटनाओं से खुद को अपंजीकृत / अलग करने की आवश्यकता है। हमेशा यह सुनिश्चित करने के लिए कि ये पहले अपंजीकृत / उन्हें अलग करने की कोशिश करने से पहले अशक्त नहीं हैं!

  5. आपकी कक्षा में उपरोक्त में से कोई भी संयोजन है ...

COM ऑब्जेक्ट्स के साथ काम करने और Marshal.ReleaseComObject () का उपयोग करने के लिए एक अनुशंसित विकल्प System.Runtime.InteropServices.SafeHandle वर्ग का उपयोग करना है।

BCL (बेस क्लास लाइब्रेरी टीम) के बारे में एक अच्छी ब्लॉग पोस्ट है यहाँ http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx

एक महत्वपूर्ण बात यह है कि यदि आप WCF के साथ काम कर रहे हैं और संसाधनों की सफाई कर रहे हैं, तो आपको ALMOST ALWAYS को 'ब्लॉक' का उपयोग करने से बचना चाहिए। वहाँ बहुत सारे ब्लॉग पोस्ट हैं और MSDN पर कुछ इस बारे में कि यह एक बुरा विचार क्यों है। मैंने इसके बारे में यहाँ पोस्ट भी किया है - WCF प्रॉक्सी के साथ 'यूज़ ()' का प्रयोग न करें


3
मेरा मानना ​​है कि 5 वां मामला है: यदि आपकी कक्षा मजबूत संदर्भों का उपयोग करके घटनाओं की सदस्यता लेती है, तो आपको आईडीसोफ़रेबल को लागू करना चाहिए और अपने आप को डिस्पोज़ विधि में घटनाओं से अपंजीकृत करना चाहिए।
डिडियर ए।

हाय दीदीबस हाँ आप सही हैं। मैं उसके बारे में भूल गया। मैंने अपने उत्तर को संशोधित करने के लिए एक मामले के रूप में शामिल किया है। धन्यवाद।
डेव ब्लैक

डिस्पोज़ पैटर्न के लिए MSDN दस्तावेज़ीकरण एक और मामला जोड़ता है: "कक्षाओं पर बुनियादी निपटान पैटर्न को लागू करने वाला कॉन्सेप्ट जो स्वयं अप्रबंधित संसाधन या डिस्पोजेबल ऑब्जेक्ट नहीं रखता है, लेकिन उपप्रकार होने की संभावना है। इसका एक बड़ा उदाहरण सिस्टम है ।IO .स्ट्रीम क्लास। हालांकि यह एक सार आधार वर्ग है जो किसी भी संसाधन को नहीं रखता है, इसके अधिकांश उपवर्ग करते हैं और इस वजह से यह इस पैटर्न को लागू करता है। "
गोनेन I

12

IDisposable के बजाय lambdas का उपयोग करना।

मैं कभी भी पूरी तरह से उपयोग / आइडिसोपॉली विचार से रोमांचित नहीं हुआ। समस्या यह है कि इसके लिए कॉलर की आवश्यकता है:

  • यह जान लें कि उन्हें आईडीसिसोपयोगी उपयोग करना चाहिए
  • 'का उपयोग' करने के लिए याद रखें।

मेरा नया पसंदीदा तरीका एक फैक्ट्री विधि और एक मेमने का उपयोग करना है

कल्पना कीजिए कि मैं SqlConnection के साथ कुछ करना चाहता हूं (ऐसा कुछ जिसे एक प्रयोग में लपेटा जाना चाहिए)। शास्त्रीय रूप से आप करेंगे

using (Var conn = Factory.MakeConnection())
{
     conn.Query(....);
}

नया रास्ता

Factory.DoWithConnection((conn)=>
{
    conn.Query(...);
}

पहले मामले में कॉलर केवल सिंटैक्स का उपयोग नहीं कर सकता था। दूसरे मामले में उपयोगकर्ता के पास कोई विकल्प नहीं है। ऐसी कोई विधि नहीं है जो SqlConnection ऑब्जेक्ट बनाती है, कॉल करने वाले को DoWithConnection का आह्वान करना चाहिए।

DoWithConnection इस तरह दिखता है

void DoWithConnection(Action<SqlConnection> action)
{
   using (var conn = MakeConnection())
   {
       action(conn);
   }
}

MakeConnection अब निजी है


2
लैम्ब्डा में चीजों को लपेटना एक अच्छा तरीका हो सकता है, लेकिन इसकी सीमाएं हैं। यह उन स्थितियों के लिए बहुत बुरा नहीं है, जहां, वास्तव में, एक वर्ग के सभी उपभोक्ता "ब्लॉक" का उपयोग करके काम करेंगे, लेकिन यह उन स्थितियों को अस्वीकार कर देगा जहां एक विधि एक वर्ग क्षेत्र में एक आईडीआईएसओफ़ायरी (या तो सीधे, या एक पुनरावृत्ति की तरह कुछ में स्टोर करेगा) )।
सुपरकाट

@ सुपरकैट आप तर्क दे सकते हैं कि संसाधन हॉगिंग चीजों के भंडारण को रोकना एक अच्छी बात है। मेरे द्वारा उधार लिया गया मॉडल मैं यहां प्रस्तुत करता हूं कि आप संसाधन के अपने उपयोग के साथ दुबले होने के लिए मजबूर हैं
pm100

यह एक अच्छी बात हो सकती है, लेकिन यह कुछ बहुत ही उचित संचालन को बहुत कठिन बना सकती है। उदाहरण के लिए, मान लें कि IEnumerable <T> को लागू करने के बजाय एक डेटाबेस-रीडर प्रकार, एक विधि को उजागर करता है DoForAll(Action<T>) where T:IComparable<T>, प्रत्येक रिकॉर्ड पर इंगित प्रतिनिधि को बुलाता है। दो ऐसी वस्तुओं को देखते हुए, जो दोनों क्रमबद्ध क्रम में डेटा लौटाएंगे, एक संग्रह में मौजूद सभी वस्तुओं का उत्पादन कैसे होगा लेकिन दूसरे का नहीं? यदि लागू किए गए प्रकार IEnumerable<T>, कोई एक मर्जिंग ऑपरेशन कर सकता है, लेकिन वह काम नहीं करेगा DoForAll
सुपरकैट

जिस तरह से मैं DoForAllपहली बार एक कॉपी करने के लिए दो संग्रह को मर्ज करने का पता लगा सकता हूं , उसकी संपूर्णता में, किसी अन्य संरचना में, दो थ्रेड्स का उपयोग करना होगा, जो कि केवल एक जोड़े का उपयोग करने की तुलना में संसाधनों का अधिक घृणित होगा IEnumerable और सावधान रहना उन्हें रिहा करने के लिए।
1

-1: एक सवाल का अच्छा जवाब जो नहीं पूछा गया था। यह एक महान जवाब होगा कि "मैं आईडीसॉप्रेटी ऑब्जेक्ट्स की खपत को आसान कैसे बनाऊं"
जॉन सॉन्डर्स

10

किसी ने भी इस सवाल का जवाब नहीं दिया कि क्या आपको आईडीस्रोपी को लागू करना चाहिए, भले ही आपको इसकी आवश्यकता न हो।

संक्षिप्त उत्तर: नहीं

लंबा जवाब:

यह आपकी कक्षा के एक उपभोक्ता को 'उपयोग' करने की अनुमति देगा। मैं जो सवाल पूछूंगा, वह है - वे ऐसा क्यों करेंगे? अधिकांश देवता 'उपयोग' का उपयोग तब तक नहीं करेंगे जब तक कि उन्हें पता न हो कि वे अवश्य हैं - और वे कैसे जानते हैं। भी

  • अनुभव से उन्हें इसका पालन करना (उदाहरण के लिए सॉकेट क्लास)
  • इसका दस्तावेज
  • वे सतर्क हैं और यह देख सकते हैं कि वर्ग आईडीसोफाइल को लागू करता है

तो आईडीआईएसओपी को लागू करने से आप देवों (कम से कम कुछ) को बता रहे हैं कि यह वर्ग कुछ ऐसा लपेटता है जिसे जारी किया जाना चाहिए। वे 'का उपयोग कर' का उपयोग करेंगे - लेकिन ऐसे अन्य मामले हैं जहां उपयोग करना संभव नहीं है (ऑब्जेक्ट का दायरा स्थानीय नहीं है); और उन्हें उन अन्य मामलों में वस्तुओं के जीवनकाल के बारे में चिंता करना शुरू करना होगा - मुझे यकीन है कि चिंता होगी। लेकिन यह आवश्यक नहीं है

आप उन्हें उपयोग करने के लिए सक्षम करने के लिए Idisposable को कार्यान्वित करते हैं, लेकिन जब तक आप उन्हें नहीं बताते, तब तक वे उपयोग नहीं कर पाएंगे।

तो यह मत करो


1
मुझे समझ में नहीं आता है कि क्यों एक आईडी आईडी का उपयोग करने वाले ऑब्जेक्ट पर प्रयोग / निपटान नहीं करेगा (जब तक कि कार्यक्रम किसी भी तरह से बाहर निकलने के बारे में नहीं है)।
एड्रिएंम

1
मुद्दा यह है कि एक देव को सभी कोड रास्तों में निपटान के लिए सभी कॉल लिखने होंगे, जिसके परिणामस्वरूप यह अप्रतिबंधित होगा। उदाहरण के लिए, अगर मैंने एक शब्दकोश में एक उदाहरण दिया है, जब मैं डिक्शनरी से प्रविष्टियों को हटाता हूं तो मुझे निपटान करना होगा। इसकी बहुत परेशानी है जो इस मामले में आवश्यक नहीं है - ऑब्जेक्ट को
निपटाने की

3
@ pm100 Re: अनावश्यक रूप से आईडीसिसोपयोगी लागू करना - codeproject.com/KB/dotnet/idisposable.aspx पर विस्तृत लेख है जिसमें कुछ दुर्लभ मामलों पर चर्चा की गई है जब आप इस बारे में सोचना चाहते हैं (बहुत दुर्लभ, मुझे यकीन है)। संक्षेप में: यदि आप भविष्य में या किसी व्युत्पन्न वस्तु में आईडीसोपायरी की आवश्यकता का पूर्वाभास कर सकते हैं, तो आप "स्लाइसिंग" के मुद्दों से बचने के लिए अपने बेस क्लास में "नो-ऑप" के रूप में आईडीस्रोपी को लागू करने के बारे में सोच सकते हैं जहाँ कुछ व्युत्पन्न वस्तुओं की आवश्यकता होती है। निपटान और अन्य नहीं।
केविन पी। राइस

4
  1. यदि आप अन्य प्रबंधित वस्तुओं का उपयोग कर रहे हैं जो अप्रबंधित संसाधनों का उपयोग कर रहे हैं, तो यह सुनिश्चित करना आपकी ज़िम्मेदारी नहीं है। आपकी जिम्मेदारी यह है कि डिस्पोज़ को उन ऑब्जेक्ट पर कॉल करें, जब डिस्पोज़ को आपकी ऑब्जेक्ट पर कॉल किया जाता है, और यह वहीं रुक जाता है।

  2. यदि आपकी कक्षा किसी भी दुर्लभ संसाधनों का उपयोग नहीं करती है, तो मैं यह देखने में विफल रहता हूं कि आप अपनी कक्षा को आइडीसोपायरी क्यों लागू करेंगे। आपको केवल इतना करना चाहिए अगर आप:

    • पता है कि आपके पास जल्द ही आपकी वस्तुओं में दुर्लभ संसाधन होंगे, अभी नहीं (और मेरा मतलब है कि जैसा कि "हम अभी भी विकसित कर रहे हैं, यह यहाँ होगा इससे पहले कि हम कर रहे हैं", जैसा कि "मुझे नहीं लगता कि हमें इसकी आवश्यकता होगी" ")
    • दुर्लभ संसाधनों का उपयोग करना
  3. हां, आपके कोड का उपयोग करने वाले कोड को आपके ऑब्जेक्ट की डिस्पोज विधि को कॉल करना होगा। और हाँ, कोड जो आपके ऑब्जेक्ट का उपयोग usingकरता है, जैसा आपने दिखाया है।

  4. (2 फिर से?) यह संभावना है कि WebClient या तो अप्रबंधित संसाधनों, या अन्य प्रबंधित संसाधनों का उपयोग करता है जो आईडीआईसोपायरी को लागू करते हैं। हालांकि, सटीक कारण महत्वपूर्ण नहीं है। यह महत्वपूर्ण है कि यह आईडीसोपॉली को लागू करता है, और इसलिए यह आपके साथ काम करने पर उस ज्ञान पर कार्य करने के लिए गिरता है, जब आप इसके साथ काम करते हैं, भले ही यह वेबक्लाइंट किसी अन्य संसाधनों का उपयोग न करता हो।


4

निपटान पैटर्न:

public abstract class DisposableObject : IDisposable
{
    public bool Disposed { get; private set;}      

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

    ~DisposableObject()
    {
        Dispose(false);
    }

    private void Dispose(bool disposing)
    {
        if (!Disposed)
        {
            if (disposing)
            {
                DisposeManagedResources();
            }

            DisposeUnmanagedResources();
            Disposed = true;
        }
    }

    protected virtual void DisposeManagedResources() { }
    protected virtual void DisposeUnmanagedResources() { }
}

विरासत का उदाहरण:

public class A : DisposableObject
{
    public Component components_a { get; set; }
    private IntPtr handle_a;

    protected override void DisposeManagedResources()
    {
        try
        {
          Console.WriteLine("A_DisposeManagedResources");
          components_a.Dispose();
          components_a = null;
        }
        finally
        { 
          base.DisposeManagedResources();
        }
    }

    protected override void DisposeUnmanagedResources()
    {
        try
        {
          Console.WriteLine("A_DisposeUnmanagedResources");
          CloseHandle(handle_a);
          handle_a = IntPtr.Zero;
        }
        finally
        { 
          base.DisposeUnmanagedResources();
        }
    }
}

public class B : A
{
    public Component components_b { get; set; }
    private IntPtr handle_b;

    protected override void DisposeManagedResources()
    {
        try
        {
          Console.WriteLine("B_DisposeManagedResources");
          components_b.Dispose();
          components_b = null;
        }
        finally
        { 
          base.DisposeManagedResources();
        }
    }

    protected override void DisposeUnmanagedResources()
    {
        try
        {
          Console.WriteLine("B_DisposeUnmanagedResources");
          CloseHandle(handle_b);
          handle_b = IntPtr.Zero;
        }
        finally
        { 
          base.DisposeUnmanagedResources();
        }
    }
}

4

एक अन्य उत्तर के कुछ पहलू 2 कारणों से थोड़े गलत हैं:

प्रथम,

using(NoGateway objNoGateway = new NoGateway())

वास्तव में इसके बराबर है:

try
{
    NoGateway = new NoGateway();
}
finally
{
    if(NoGateway != null)
    {
        NoGateway.Dispose();
    }
}

यह हास्यास्पद लग सकता है क्योंकि 'नया' ऑपरेटर को कभी भी 'अशक्त' नहीं लौटना चाहिए, जब तक कि आपके पास आउटऑफमेरी अपवाद न हो। लेकिन निम्नलिखित मामलों पर विचार करें: 1. आप एक FactoryClass को कॉल करते हैं जो एक आईडीसिसोप्लोब रिसोर्स को लौटाता है या 2. यदि आपके पास एक प्रकार है जो कि इसके कार्यान्वयन के आधार पर आईडीसोर्पोट से विरासत में मिला हो सकता है या नहीं भी हो सकता है - याद रखें कि मैंने देखा है कि आईडीआईसोपोलेटरी पैटर्न गलत तरीके से लागू किया गया है कई क्लाइंट्स में, जहां डेवलपर्स सिर्फ आईडीसोपोलेटरी (खराब, खराब, खराब) से विरासत में लिए बिना एक डिस्पोज () पद्धति जोड़ते हैं। आपके पास एक संपत्ति या विधि (फिर से बुरा, बुरा, बुरा - से दूर होने का एक IDisposable संसाधन का मामला भी हो सकता है - अपने IDisposable संसाधनों को न दें)

using(IDisposable objNoGateway = new NoGateway() as IDisposable)
{
    if (NoGateway != null)
    {
        ...

यदि '' के रूप में 'ऑपरेटर अशक्त (या संपत्ति या संसाधन को लौटाने की विधि) देता है, और' कोड 'ब्लॉक का उपयोग करते हुए' null 'से बचाता है, तो कोड को कॉल करने की कोशिश करने पर आपका कोड नहीं फूटेगा क्योंकि इसकी वजह से एक अशक्त वस्तु का निपटान होता है। 'अंतर्निर्मित' अशक्त जाँच।

आपका उत्तर सही नहीं होने का दूसरा कारण निम्नलिखित stmt है:

एक अंतिम रूप से कहा जाता है कि जीसी आपकी वस्तु को नष्ट कर रहा है

पहला, अंतिम रूप (साथ ही जीसी स्वयं) गैर-नियतात्मक है। यह सीएलआर निर्धारित करता है कि यह एक अंतिम फोन कब करेगा। डेवलपर / कोड का कोई मतलब नहीं है। यदि आईडीसोपोलेटरी पैटर्न सही तरीके से लागू किया गया है (जैसा कि मैंने ऊपर पोस्ट किया है) और GC.SuppressFinalize () को बुलाया गया है, तो फाइनलिज़र को नहीं बुलाया जाएगा। पैटर्न को सही ढंग से लागू करने के लिए यह एक बड़ा कारण है। चूँकि लॉजिकल प्रोसेसरों की संख्या की परवाह किए बिना प्रति प्रबंधित प्रक्रिया में केवल 1 फाइनल थ्रेड है, आप GC.SuppressFinalize () को कॉल करने के लिए भूलकर भी फ़ाइनलीज़र को बैक-अप करके या यहाँ तक कि हैंग करके भी प्रदर्शन को कम कर सकते हैं।

मैंने अपने ब्लॉग पर डिस्पोज़ पैटर्न का एक सही कार्यान्वयन पोस्ट किया है: डिस्पोज़ पैटर्न को ठीक से कैसे लागू किया जाए


2
क्या आप लिखने के बारे में निश्चित हैं NoGateway = new NoGateway();और NoGateway != null?
कूर

1
क्या यह stackoverflow.com/a/898856/3195477 का जिक्र कर रहा था ? अब कोई जवाब नहीं है जिसका नाम 'Icey' है
UuDdLrLrSs

@DaveInCaz ऐसा लगता है कि सही है। मुझे कहीं भी 'Icey' दिखाई नहीं देता है, लेकिन मेरी प्रतिक्रिया का संदर्भ ऊपर दिए गए लिंक द्वारा दिए गए उत्तर पर निर्देशित किया गया लगता है। शायद उसने अपना उपयोगकर्ता नाम बदल दिया?
डेव ब्लैक

@DaveBlack कूल, धन्यवाद। मैंने अभी उस लेख को संपादित किया है।
UUDdLrLrSs

2

1) WebClient एक प्रबंधित प्रकार है, इसलिए आपको अंतिम रूप देने की आवश्यकता नहीं है। अंतिम स्थिति में आपके उपयोगकर्ताओं को आपके NoGateway वर्ग के निपटान () और मूल प्रकार (जो GC द्वारा एकत्र नहीं किया जाता है) को साफ करने की आवश्यकता होती है। इस स्थिति में, यदि उपयोगकर्ता Dispose () को कॉल नहीं करता है, तो निहित WebClient को GCG द्वारा NoGateway के ठीक बाद भेज दिया जाएगा।

2) अप्रत्यक्ष रूप से हाँ, लेकिन आपको इसके बारे में चिंता नहीं करनी चाहिए। आपका कोड स्टैंड के रूप में सही है और आप अपने उपयोगकर्ताओं को बहुत आसानी से डिस्पोज़ () करने से नहीं रोक सकते।


2

Msdn से पैटर्न

public class BaseResource: IDisposable
{
   private IntPtr handle;
   private Component Components;
   private bool disposed = false;
   public BaseResource()
   {
   }
   public void Dispose()
   {
      Dispose(true);      
      GC.SuppressFinalize(this);
   }
   protected virtual void Dispose(bool disposing)
   {
      if(!this.disposed)
      {        
         if(disposing)
         {
            Components.Dispose();
         }         
         CloseHandle(handle);
         handle = IntPtr.Zero;
       }
      disposed = true;         
   }
   ~BaseResource()      
   {      Dispose(false);
   }
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}
public class MyResourceWrapper: BaseResource
{
   private ManagedResource addedManaged;
   private NativeResource addedNative;
   private bool disposed = false;
   public MyResourceWrapper()
   {
   }
   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {             
               addedManaged.Dispose();         
            }
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            base.Dispose(disposing);
         }
      }
   }
}

1
using(NoGateway objNoGateway = new NoGateway())

के बराबर है

try
{
    NoGateway = new NoGateway();
}

finally
{
    NoGateway.Dispose();
}

एक अंतिम रूप से कहा जाता है कि जीसी आपकी वस्तु को नष्ट कर रहा है। जब आप अपना तरीका छोड़ते हैं तो यह बिलकुल अलग समय पर हो सकता है। आपके द्वारा उपयोग किए जाने वाले ब्लॉक को छोड़ने के तुरंत बाद आईडीसोपायरी का निपटान कहा जाता है। इसलिए पैटर्न आमतौर पर उपयोग करने के लिए उपयोग करने के लिए मुफ्त ressources के तुरंत बाद आप उन्हें अब और ज़रूरत नहीं है।


1
GC को ऑब्जेक्ट को नष्ट करने पर एक अंतिम रूप नहीं कहा जाता है। यदि "अंतिम रूप" को ओवरराइड किया जाता है, तो जब जीसी ने अन्यथा ऑब्जेक्ट को नष्ट कर दिया होगा, तो इसे अंतिम रूप देने वाली वस्तुओं की एक कतार पर रखा जाएगा, अस्थायी रूप से इसे एक मजबूत संदर्भ बनाने और - कम से कम अस्थायी रूप से - "इसे फिर से जीवित करना"।
सुपरकैट

-5

जो मुझे पता है, उससे फाइनली / डिस्ट्रॉक्टर का उपयोग न करने की अत्यधिक अनुशंसा की जाती है:

public ~MyClass() {
  //dont use this
}

अधिकतर, यह पता नहीं होने के कारण है कि कब या क्या इसे कहा जाएगा। डिस्पोज विधि बहुत बेहतर है, खासकर यदि आप हमें सीधे उपयोग करते हैं या डिस्पोज करते हैं।

का उपयोग करना अच्छा है। इसका इस्तेमाल करें :)


2
आपको इनकोप के उत्तर के लिंक का पालन करना चाहिए। हां, उपयोग / निपटान बेहतर है लेकिन एक डिस्पोजेबल वर्ग को दोनों को लागू करना चाहिए।
हेनक होल्टरमैन २२'०

दिलचस्प है, सभी डॉक्स जो मैंने Microsoft से पढ़े हैं - जैसे कि रूपरेखा डिज़ाइन दिशा-निर्देश - कहते हैं कि कभी भी एक विध्वंसक का उपयोग न करें। हमेशा आईडिस्पोजेबल का उपयोग करें।
निक वाइज

5
बस एक कक्षा का उपयोग करने और कक्षा को लिखने के बीच अंतर करें , उन्हें फिर से पढ़ें।
हेंक होल्टरमैन

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.