c# - सी#में पैटर्न को अंतिम रूप/डिस्पोजेक्ट करें




.net idisposable (9)

सी # 2008

मैं थोड़ी देर के लिए इस पर काम कर रहा हूं, और मैं अभी भी कुछ मुद्दों के बारे में उलझन में हूं। मेरे प्रश्न नीचे हैं

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

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

    क्या यह 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. यहां मैंने फाइनलाइज़र नहीं जोड़ा है, और आम तौर पर फाइनलर जीसी द्वारा बुलाया जाएगा, और फाइनलजर निपटान को बुलाएगा। चूंकि मेरे पास फाइनलाइज़र नहीं है, तो मैं निपटान विधि कब कहूं? क्या यह कक्षा का ग्राहक है जिसे इसे कॉल करना है?

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

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

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

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


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

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

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

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


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

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


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

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

  • पता है कि उन्हें IDisposable का उपयोग करना चाहिए
  • 'उपयोग' का उपयोग करना याद रखें।

मेरी नई पसंदीदा विधि फ़ैक्टरी विधि और इसके बजाय लैम्ब्डा का उपयोग करना है

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

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

नया रास्ता

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

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

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

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

MakeConnection अब निजी है


अनुशंसित IDisposable पैटर्न here । IDISposable का उपयोग करने वाली कक्षा को प्रोग्राम करते समय, आमतौर पर आपको दो पैटर्न का उपयोग करना चाहिए:

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

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 को कॉल करता है जो अंतिम रूप को लागू करता है।

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


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

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

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

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


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

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

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

#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

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

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

मैंने अपने ब्लॉग पर इस कार्यान्वयन को यहां पोस्ट किया है: निपटान पैटर्न को उचित रूप से कार्यान्वित कैसे करें


मैं pm100 के साथ सहमत हूं (और इसे मेरी पिछली पोस्ट में स्पष्ट रूप से कहा जाना चाहिए था)।

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

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

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

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

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

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

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

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

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


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

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
  }

}

using(NoGateway objNoGateway = new NoGateway())

के बराबर है

try
{
    NoGateway = new NoGateway();
}

finally
{
    NoGateway.Dispose();
}

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





finalizer