سؤال

سيقوم مجمّع البيانات المهملة .NET في النهاية بتحرير الذاكرة، ولكن ماذا لو كنت تريد استعادة تلك الذاكرة على الفور؟ما هو الرمز الذي تحتاج إلى استخدامه في الفصل الدراسي MyClass للإتصال

MyClass.Dispose()

وتحرير كل المساحة المستخدمة بواسطة المتغيرات والكائنات الموجودة MyClass?

هل كانت مفيدة؟

المحلول

IDisposable ليس له علاقة بتحرير الذاكرة.IDisposable هو نمط للتحرر غير مُدار الموارد - والذاكرة هي بالتأكيد مورد مُدار.

الارتباطات التي تشير إلى GC.Collect() هي الإجابة الصحيحة، على الرغم من عدم تشجيع استخدام هذه الوظيفة بشكل عام بواسطة وثائق Microsoft .NET.

يحرر: بعد أن حصلت على قدر كبير من الكارما لهذه الإجابة، أشعر بمسؤولية معينة لتوضيحها، خشية أن يحصل الوافد الجديد إلى إدارة موارد .NET على انطباع خاطئ.

داخل عملية .NET، هناك نوعان من الموارد - المُدارة وغير المُدارة."مُدار" يعني أن وقت التشغيل يتحكم في المورد، في حين أن "غير مُدار" يعني أن هذا هو مسؤولية المبرمج.ويوجد بالفعل نوع واحد فقط من الموارد المُدارة التي نهتم بها في .NET اليوم - الذاكرة.يخبر المبرمج وقت التشغيل بتخصيص الذاكرة وبعد ذلك يعود الأمر إلى وقت التشغيل لمعرفة متى يمكن تحرير الذاكرة.تسمى الآلية التي يستخدمها .NET لهذا الغرض جمع القمامة ويمكنك العثور على الكثير من المعلومات حول GC على الإنترنت ببساطة عن طريق استخدام Google.

بالنسبة للأنواع الأخرى من الموارد، لا يعرف .NET أي شيء عن تنظيفها، لذا يجب عليه الاعتماد على المبرمج للقيام بالشيء الصحيح.ولتحقيق هذه الغاية، توفر المنصة للمبرمج ثلاث أدوات:

  1. واجهة IDisposable وبيان "استخدام" في VB وC#
  2. اللمسات النهائية
  3. نمط IDisposable كما تم تنفيذه بواسطة العديد من فئات BCL

أول هذه العناصر يسمح للمبرمج بالحصول على المورد بكفاءة، واستخدامه ثم إطلاقه جميعًا بنفس الطريقة.

using (DisposableObject tmp = DisposableObject.AcquireResource()) {
    // Do something with tmp
}
// At this point, tmp.Dispose() will automatically have been called
// BUT, tmp may still a perfectly valid object that still takes up memory

إذا كان "AcquireResource" هو أسلوب المصنع الذي (على سبيل المثال) يفتح ملفًا ويقوم "Dispose" بإغلاق الملف تلقائيًا، فلا يمكن لهذا الرمز تسريب مورد ملف.ولكن قد لا يزال من الممكن تخصيص ذاكرة كائن "tmp" نفسه.وذلك لأن واجهة IDisposable ليس لها أي اتصال على الإطلاق بأداة تجميع البيانات المهملة.اذا أنت فعل إذا كنت تريد التأكد من تحرير الذاكرة، فسيكون خيارك الوحيد هو الاتصال GC.Collect() لإجبار جمع القمامة.

ومع ذلك، لا يمكن التأكيد بما فيه الكفاية على أن هذه ربما ليست فكرة جيدة.من الأفضل عمومًا السماح لمجمع البيانات المهملة بالقيام بما تم تصميمه للقيام به، وهو إدارة الذاكرة.

ماذا يحدث إذا تم استخدام المورد لفترة زمنية أطول، بحيث يتجاوز عمره عدة طرق؟من الواضح أن عبارة "الاستخدام" لم تعد قابلة للتطبيق، لذا سيتعين على المبرمج استدعاء "التخلص" يدويًا عند الانتهاء من استخدام المورد.وماذا يحدث إذا نسي المبرمج؟إذا لم يكن هناك بديل، فقد تنفد العملية أو الكمبيوتر في النهاية من أي مورد لم يتم تحريره بشكل صحيح.

هذا هو المكان الذي تأتي فيه اللمسات النهائية.أداة الإنهاء هي طريقة في صفك لها علاقة خاصة بأداة تجميع البيانات المهملة.يعد GC بأنه - قبل تحرير الذاكرة لأي كائن من هذا النوع - سيعطي أولاً فرصة لأداة الإنهاء لإجراء نوع من التنظيف.

لذا، في حالة الملف، لا نحتاج نظريًا إلى إغلاق الملف يدويًا على الإطلاق.يمكننا فقط الانتظار حتى يصل جامع البيانات المهملة إليه ثم نترك أداة الإنهاء للقيام بالعمل.لسوء الحظ، هذا لا يعمل بشكل جيد في الممارسة العملية لأن مجمع البيانات المهملة يعمل بطريقة غير حتمية.قد يظل الملف مفتوحًا لفترة أطول بكثير مما يتوقعه المبرمج.وإذا تم الاحتفاظ بما يكفي من الملفات مفتوحة، فقد يفشل النظام عند محاولة فتح ملف إضافي.

بالنسبة لمعظم الموارد، نريد كلا هذين الأمرين.نريد أن تكون الاتفاقية قادرة على القول "لقد انتهينا من هذا المورد الآن" ونريد التأكد من أن هناك على الأقل فرصة لحدوث عملية التنظيف تلقائيًا إذا نسينا القيام بذلك يدويًا.وهنا يأتي دور نمط "IDisposable".هذا هو التقليد الذي يسمح لـ IDispose وأداة الإنهاء باللعب معًا بشكل جيد.يمكنك أن ترى كيف يعمل النمط من خلال النظر إلى الوثائق الرسمية لـ IDisposable.

الحد الأدنى: إذا كان ما تريد فعله حقًا هو التأكد من تحرير الذاكرة، فلن يساعدك IDisposable و Finalizers.لكن واجهة IDisposable هي جزء من نمط مهم للغاية يجب أن يفهمه جميع مبرمجي .NET.

نصائح أخرى

يمكنك فقط التخلص من المثيلات التي تقوم بتنفيذ واجهة IDisposable.

لفرض تجميع البيانات المهملة لتحرير الذاكرة (غير المُدارة) على الفور:

GC.Collect();  
GC.WaitForPendingFinalizers();

يعد هذا عادةً ممارسة سيئة، ولكن يوجد على سبيل المثال خطأ في الإصدار x64 من إطار عمل .NET يجعل GC يتصرف بشكل غريب في بعض السيناريوهات، ومن ثم قد ترغب في القيام بذلك.لا أعرف إذا كان قد تم حل الخلل بعد.هل يعلم أحد؟

للتخلص من فئة يمكنك القيام بذلك:

instance.Dispose();

او مثل هذا:

using(MyClass instance = new MyClass())
{
    // Your cool code.
}

والتي سيتم ترجمتها في وقت الترجمة إلى:

MyClass instance = null;    

try
{
    instance = new MyClass();        
    // Your cool code.
}
finally
{
    if(instance != null)
        instance.Dispose();
}

يمكنك تنفيذ واجهة IDisposable مثل هذا:

public class MyClass : IDisposable
{
    private bool disposed;

    /// <summary>
    /// Construction
    /// </summary>
    public MyClass()
    {
    }

    /// <summary>
    /// Destructor
    /// </summary>
    ~MyClass()
    {
        this.Dispose(false);
    }

    /// <summary>
    /// The dispose method that implements IDisposable.
    /// </summary>
    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// The virtual dispose method that allows
    /// classes inherithed from this one to dispose their resources.
    /// </summary>
    /// <param name="disposing"></param>
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources here.
            }

            // Dispose unmanaged resources here.
        }

        disposed = true;
    }
}

لقد أصبحت الإجابات على هذا السؤال أكثر من مشوشة بعض الشيء.

يسأل العنوان عن التخلص، لكنه يقول بعد ذلك أنهم يريدون استعادة الذاكرة على الفور.

.نت هو تمكنت, مما يعني أنه عند كتابة تطبيقات .Net، لا داعي للقلق بشأن الذاكرة مباشرة، والتكلفة هي أنه ليس لديك تحكم مباشر في الذاكرة أيضًا.

.Net هو الذي يقرر متى يكون من الأفضل تنظيف الذاكرة وتحريرها، وليس أنت كمبرمج .Net.

ال Dispose هي طريقة لإخبار .Net أنك انتهيت من شيء ما، لكنها لن تحرر الذاكرة حتى يحين الوقت المناسب للقيام بذلك.

بشكل أساسي، سيقوم .Net بجمع الذاكرة مرة أخرى عندما يكون من الأسهل عليه القيام بذلك - وهو جيد جدًا في تحديد الوقت المناسب.ما لم تكن تكتب شيئًا يستهلك الكثير من الذاكرة، فلن تحتاج عادةً إلى نقضه (وهذا جزء من السبب وراء عدم كتابة الألعاب غالبًا على .Net حتى الآن - فهي تحتاج إلى تحكم كامل)

في .Net يمكنك استخدامها GC.Collect() لإجبارها على الفور، ولكن هذه ممارسة سيئة دائمًا تقريبًا.إذا لم يقم .Net بتنظيفه بعد، فهذا يعني أن الوقت ليس مناسبًا للقيام بذلك.

GC.Collect() يلتقط الكائنات التي يحددها .Net كما تم القيام بها.إذا لم تقم بالتخلص من كائن يحتاج إليه. فقد تقرر Net الاحتفاظ بهذا الكائن.هذا يعني ذاك GC.Collect() يكون فعالاً فقط إذا قمت بتنفيذ المثيلات التي يمكن التخلص منها بشكل صحيح.

GC.Collect() يكون لا بديل لاستخدام IDisposable بشكل صحيح.

لذا فإن التخلص والذاكرة ليسا مرتبطين بشكل مباشر، لكن ليس من الضروري أن يكونا كذلك.سيؤدي التخلص بشكل صحيح إلى جعل تطبيقات .Net الخاصة بك أكثر كفاءة وبالتالي تستخدم ذاكرة أقل.


99% من الوقت الذي تقضيه في .Net هو أفضل الممارسات التالية:

المادة 1: إذا كنت لا تتعامل مع أي شيء غير مُدار أو الذي ينفذ IDisposable ثم لا تقلق بشأن التخلص.

القاعدة 2: إذا كان لديك متغير محلي يقوم بتنفيذ IDisposable، فتأكد من التخلص منه في النطاق الحالي:

//using is best practice
using( SqlConnection con = new SqlConnection("my con str" ) )
{
    //do stuff
} 

//this is what 'using' actually compiles to:
SqlConnection con = new SqlConnection("my con str" ) ;
try
{
    //do stuff
}
finally
{
    con.Dispose();
}

القاعدة 3: إذا كانت الفئة تحتوي على خاصية أو متغير عضو يقوم بتنفيذ IDisposable، فيجب أن تقوم تلك الفئة بتطبيق IDisposable أيضًا.في طريقة التخلص الخاصة بهذه الفئة، يمكنك أيضًا التخلص من خصائص IDisposable الخاصة بك:

//rather basic example
public sealed MyClass :
   IDisposable
{   
    //this connection is disposable
    public SqlConnection MyConnection { get; set; }

    //make sure this gets rid of it too
    public Dispose() 
    {
        //if we still have a connection dispose it
        if( MyConnection != null )
            MyConnection.Dispose();

        //note that the connection might have already been disposed
        //always write disposals so that they can be called again
    }
}

هذا ليس كاملاً حقًا، ولهذا السبب تم إغلاق المثال.قد تحتاج الفصول الموروثة إلى مراعاة القاعدة التالية...

القاعدة 4: إذا كان الفصل يستخدم غير مُدار المورد ثم قم بتنفيذ IDispose و إضافة النهائي.

.Net لا يمكنه فعل أي شيء مع غير مُدار الموارد، والآن نحن نتحدث عن الذاكرة.إذا لم تقم بتنظيفه، يمكنك الحصول على تسرب للذاكرة.

تحتاج طريقة التخلص إلى التعامل مع كليهما تمكنت و غير مُدار موارد.

تعتبر أداة الإنهاء بمثابة أداة أمان - فهي تضمن أنه إذا قام شخص آخر بإنشاء مثيل لفصلك وفشل في التخلص منه، فإن ذلك يعد "خطيرًا". غير مُدار لا يزال من الممكن تنظيف الموارد بواسطة .Net.

~MyClass()
{
    //calls a protected method 
    //the false tells this method
    //not to bother with managed
    //resources
    this.Dispose(false);
}

public void Dispose()
{
    //calls the same method
    //passed true to tell it to
    //clean up managed and unmanaged 
    this.Dispose(true);

    //as dispose has been correctly
    //called we don't need the 

    //'backup' finaliser
    GC.SuppressFinalize(this);
}

أخيرًا هذا الحمل الزائد للتخلص الذي يأخذ علامة منطقية:

protected virtual void Dispose(bool disposing)
{
    //check this hasn't been called already
    //remember that Dispose can be called again
    if (!disposed)
    {
        //this is passed true in the regular Dispose
        if (disposing)
        {
            // Dispose managed resources here.
        }

        //both regular Dispose and the finaliser
        //will hit this code
        // Dispose unmanaged resources here.
    }

    disposed = true;
}

لاحظ أنه بمجرد وضع كل هذا في مكانه الصحيح، يمكن للتعليمات البرمجية المُدارة الأخرى التي تنشئ مثيلًا لفصلك أن تتعامل معه مثل أي معرف آخر (القاعدتان 2 و3).

هل من المناسب أن نذكر أيضًا أن التخلص لا يشير دائمًا إلى الذاكرة؟أتخلص من الموارد مثل هذه الإشارات إلى الملفات في كثير من الأحيان من الذاكرة.يرتبط GC.Collect() مباشرة بمجمع البيانات المهملة CLR وقد يقوم أو لا يقوم بتحرير الذاكرة (في إدارة المهام).من المحتمل أن يؤثر ذلك على طلبك بطرق سلبية (مثل الأداء).

في نهاية المطاف، لماذا تريد استعادة الذاكرة على الفور؟إذا كان هناك ضغط على الذاكرة من مكان آخر، فسيحصل نظام التشغيل على الذاكرة في معظم الحالات.

تفقد هذا شرط

إن تنفيذ نمط التخلص و/أو IDisposable و/أو أداة الإنهاء ليس له أي علاقة على الإطلاق بوقت استعادة الذاكرة؛بدلاً من ذلك، يتعلق الأمر بإخبار GC كيف لاستعادة تلك الذاكرة.عند استدعاء Dispose() فإنك لا تتفاعل بأي حال من الأحوال مع GC.

سيتم تشغيل GC فقط عندما يحدد الحاجة إلى (يسمى ضغط الذاكرة) وبعد ذلك (وعندها فقط) سيقوم بإلغاء تخصيص الذاكرة للكائنات غير المستخدمة وضغط مساحة الذاكرة.

أنت استطاع اتصل بـ GC.Collect() ولكن لا ينبغي عليك ذلك إلا إذا كان هناك جداً سبب وجيه لـ (والذي يكون دائمًا تقريبًا "أبدًا").عندما تفرض دورة تجميع خارج النطاق مثل هذه، فإنك تتسبب في قيام GC بالمزيد من العمل ويمكن أن يؤدي في النهاية إلى الإضرار بأداء تطبيقاتك.طوال مدة دورة تجميع GC، يكون تطبيقك في حالة تجميد فعليًا... كلما زاد عدد دورات GC التي يتم تشغيلها، زاد الوقت الذي يقضيه تطبيقك مجمداً.

هناك أيضًا بعض استدعاءات Win32 API الأصلية التي يمكنك إجراؤها لتحرير مجموعة العمل الخاصة بك، ولكن حتى تلك يجب تجنبها ما لم يكن هناك جداً سبب وجيه للقيام بذلك.

الفرضية الكاملة وراء وقت التشغيل الذي تم جمعه من البيانات المهملة هي أنك لا تحتاج إلى القلق (بالقدر نفسه) بشأن وقت تخصيص/إلغاء تخصيص الذاكرة الفعلية في وقت التشغيل؛ما عليك سوى القلق بشأن التأكد من أن الكائن الخاص بك يعرف كيفية التنظيف بعد أن يُطلب منك ذلك.

public class MyClass : IDisposable
{
    public void Dispose()
    {
       // cleanup here
    }
}

ثم يمكنك أن تفعل شيئا من هذا القبيل

MyClass todispose = new MyClass();
todispose.Dispose(); // instance is disposed right here

أو

using (MyClass instance = new MyClass())
{

}
// instance will be disposed right here as it goes out of scope

شرح كامل من جو دافي على "التخلص والإنهاء وإدارة الموارد":

في وقت سابق من عمر .NET Framework ، تمت الإشارة باستمرار إلى المدمرين من قبل المبرمجين C#.عندما أصبحنا أكثر ذكاءً بمرور الوقت ، نحاول أن نتصالح مع حقيقة أن الطريقة التخلص من الأسلوب هي في الحقيقة أكثر تعادلًا لمدمار C ++ (حتمي), ، بينما ال Finalizer شيء منفصل تمامًا (غير محدد).حقيقة أن C# استعارت بناء جملة C ++ Destructor (أي~ t ()) بالتأكيد كان على الأقل علاقة صغيرة على الأقل بتطوير هذا التسمية الخاطئة.

لقد كتبت ملخصًا عن المدمرات والتخلص وجمع القمامة في http://codingcraftsman.wordpress.com/2012/04/25/to-dispose-or-not-to-dispose/

للإجابة على السؤال الأصلي:

  1. لا تحاول إدارة ذاكرتك
  2. لا يتعلق التخلص بإدارة الذاكرة، بل يتعلق بإدارة الموارد غير المُدارة
  3. تعد أدوات الإنهاء جزءًا فطريًا من نمط التخلص وهي في الواقع تبطئ عملية تحرير الذاكرة للكائنات المُدارة (حيث يتعين عليها الانتقال إلى قائمة انتظار الإنهاء ما لم يتم التخلص من d بالفعل)
  4. يعد GC.Collect سيئًا لأنه يجعل بعض الكائنات قصيرة العمر تبدو مطلوبة لفترة أطول وبالتالي يبطئ عملية جمعها.

ومع ذلك، يمكن أن يكون GC.Collect مفيدًا إذا كان لديك قسم مهم للأداء من التعليمات البرمجية وأردت تقليل احتمالية إبطاء مجموعة البيانات المهملة له.أنت تسمي ذلك من قبل.

وفوق كل ذلك، هناك حجة لصالح هذا النمط:

var myBigObject = new MyBigObject(1);
// something happens
myBigObject = new MyBigObject(2);
// at the above line, there are temporarily two big objects in memory and neither can be collected

ضد

myBigObject = null; // so it could now be collected
myBigObject = new MyBigObject(2);

لكن الإجابة الرئيسية هي أن مجموعة البيانات المهملة تعمل فقط ما لم تعبث بها!

لا يمكنك حقًا إجبار GC على تنظيف كائن عندما تريد، على الرغم من وجود طرق لإجباره على التشغيل، لا شيء يقول إنه ينظف كل الكائنات التي تريدها/تتوقعها.من الأفضل استدعاء التخلص بطريقة محاولة الالتقاط ex أخيرًا التخلص من نهاية المحاولة (VB.NET rulz).لكن التخلص مخصص لتنظيف موارد النظام (الذاكرة، والمقابض، واتصالات قاعدة البيانات، وما إلى ذلك).تخصيصها من قبل الكائن بطريقة حتمية.لا يقوم التخلص (ولا يمكنه) بتنظيف الذاكرة التي يستخدمها الكائن نفسه، فقط GC يمكنه القيام بذلك.

هذا المقال لديه إرشادات واضحة ومباشرة.لكن، نأخذ يعد استدعاء GC بدلاً من السماح له بأخذ مساره الطبيعي علامة عامة على سوء التصميم/إدارة الذاكرة، خصوصاً إذا لم يتم استهلاك أي موارد محدودة (الاتصالات والمقابض وأي شيء آخر يؤدي عادةً إلى تنفيذ IDisposable).

ما الذي يجعلك بحاجة إلى القيام بذلك؟

آسف ولكن الإجابة المحددة هنا غير صحيحة.كما ذكر عدد قليل من الأشخاص لاحقًا، فإن التخلص من IDisposable وتنفيذه لا علاقة له بتحرير الذاكرة المرتبطة بفئة .NET.يتم استخدامه بشكل أساسي وتقليدي لتحرير الموارد غير المُدارة مثل مقابض الملفات وما إلى ذلك.

بينما يمكن لتطبيقك استدعاء GC.Collect() لمحاولة فرض مجموعة بواسطة أداة تجميع البيانات المهملة، سيكون لهذا تأثير فقط على تلك العناصر الموجودة في مستوى الإنشاء الصحيح في قائمة الانتظار القابلة للاختراق.لذا فمن الممكن أنه إذا قمت بمسح كافة المراجع الخاصة بالكائن، فمن الممكن أن تحتاج إلى عدة استدعاءات لـ GC.Collect() قبل تحرير الذاكرة الفعلية.

لم تقل في سؤالك لماذا تشعر بالحاجة إلى تحرير الذاكرة على الفور.أدرك أنه في بعض الأحيان قد تكون هناك ظروف غير عادية، ولكن على محمل الجد، في التعليمات البرمجية المُدارة، من الأفضل دائمًا السماح لوقت التشغيل بالتعامل مع إدارة الذاكرة.

ربما تكون أفضل نصيحة إذا كنت تعتقد أن الكود الخاص بك يستخدم الذاكرة بشكل أسرع من قيام GC بتحريرها، فيجب عليك مراجعة الكود الخاص بك للتأكد من عدم الإشارة إلى أي كائنات لم تعد هناك حاجة إليها في أي هياكل بيانات موجودة لديك في الأعضاء الثابتة وما إلى ذلك .حاول أيضًا تجنب المواقف التي يكون لديك فيها مراجع كائنات دائرية لأنه من الممكن ألا يتم تحريرها أيضًا.

@ كيث،

أنا أتفق مع جميع القواعد الخاصة بك باستثناء رقم 4.يجب أن تتم إضافة أداة الإنهاء فقط في ظل ظروف محددة جدًا.إذا كان الفصل يستخدم موارد غير مُدارة، فيجب تنظيفها في وظيفة Dispose(bool) الخاصة بك.يجب أن تقوم هذه الوظيفة نفسها بتنظيف الموارد المُدارة فقط عندما يكون المنطق صحيحًا.تضيف إضافة أداة الإنهاء تكلفة تعقيد لاستخدام الكائن الخاص بك، حيث أنه في كل مرة تقوم فيها بإنشاء مثيل جديد، يجب أيضًا وضعه في قائمة انتظار الإنهاء، والتي يتم التحقق منها في كل مرة يقوم فيها GC بتشغيل دورة تجميع.على نحو فعال، هذا يعني أن الكائن الخاص بك سيبقى على قيد الحياة لدورة/جيل أطول مما ينبغي حتى يمكن تشغيل أداة الإنهاء.ولا ينبغي اعتبار أداة الإنهاء بمثابة "شبكة أمان".

لن يقوم GC بتشغيل دورة تجميع إلا عندما يحدد عدم وجود ذاكرة كافية متوفرة في كومة Gen0 لإجراء التخصيص التالي، إلا إذا "ساعدته" عن طريق استدعاء GC.Collect() لفرض مجموعة خارج النطاق .

خلاصة القول هي أنه بغض النظر عن الأمر، فإن GC يعرف فقط كيفية تحرير الموارد عن طريق استدعاء طريقة التخلص (وربما أداة الإنهاء إذا تم تنفيذها).الأمر متروك لهذه الطريقة "للقيام بالشيء الصحيح" وتنظيف أي موارد غير مُدارة مستخدمة وتوجيه أي موارد مُدارة أخرى لاستدعاء طريقة التخلص الخاصة بها.إنه فعال للغاية فيما يفعله ويمكنه التحسين الذاتي إلى حد كبير طالما لم تساعده دورات التجميع خارج النطاق.ومع ذلك، باستثناء استدعاء GC.Collect بشكل صريح، ليس لديك أي سيطرة على متى وبأي ترتيب سيتم التخلص من الكائنات وتحرير الذاكرة.

إذا قام MyClass بتطبيق IDisposable، فيمكنك القيام بذلك.

MyClass.Dispose();

أفضل الممارسات في C# هي:

using( MyClass x = new MyClass() ) {
    //do stuff
}

حيث يؤدي ذلك إلى اختتام عملية التخلص في المحاولة النهائية والتأكد من عدم تفويتها أبدًا.

إذا كنت لا تريد (أو لا تستطيع) تنفيذ IDisposable على الفصل الدراسي الخاص بك، فيمكنك فرض جمع البيانات المهملة مثل هذا (لكنه بطيء) -

GC.Collect();

واجهة IDisposable مخصصة حقًا للفئات التي تحتوي على موارد غير مُدارة.إذا كان فصلك لا يحتوي على موارد غير مُدارة، فلماذا تفعل ذلك؟ يحتاج لتحرير الموارد قبل أن يفعل جامع القمامة ذلك؟بخلاف ذلك، تأكد فقط من إنشاء كائنك في وقت متأخر قدر الإمكان وخروجه عن النطاق في أسرع وقت ممكن.

يمكن أن يكون لديك تدمير كائن حتمي في C ++

لن ترغب أبدًا في الاتصال بـ GC.Collect، فهو يعبث بالضبط الذاتي لمجمع البيانات المهملة لاكتشاف ضغط الذاكرة وفي بعض الحالات لا يفعل شيئًا سوى زيادة الجيل الحالي لكل كائن في الكومة.

لأولئك الذين ينشرون إجابات IDisposable.لا يؤدي استدعاء أسلوب التخلص إلى تدمير الكائن كما يصفه السائل.

@ كيث:

IDisposable مخصص للموارد المُدارة.

أدوات الإنهاء مخصصة للموارد غير المُدارة.

آسف ولكن هذا مجرد خطأ.عادة، لا يقوم برنامج الإنهاء بأي شيء على الإطلاق.ومع ذلك، إذا نمط التخلص تم تنفيذه بشكل صحيح، تحاول أداة الإنهاء أن تستدعي Dispose.

Dispose لديه وظيفتين:

  • الموارد الحرة غير المدارة، و
  • الموارد المدارة المتداخلة المجانية.

وهنا يأتي دور بيانك لأنه صحيح أنه أثناء الانتهاء، يجب ألا يحاول الكائن مطلقًا تحرير الموارد المُدارة المتداخلة، حيث قد يكون تم تحريرها بالفعل.لا يزال يتعين عليه تحرير الموارد غير المُدارة بالرغم من ذلك.

ومع ذلك، ليس لدى المتأهلين للنهائيات أي وظيفة سوى الاتصال Dispose وأخبره بعدم لمس الكائنات المُدارة. Dispose, ، عند الاتصال يدويًا (أو عبر Using)، يجب تحرير جميع الموارد غير المُدارة وتمرير الملف Dispose إرسال رسالة إلى الكائنات المتداخلة (وأساليب الفئة الأساسية) ولكن هذا سوف أبداً تحرير أي ذاكرة (المدارة).

كونراد رودولف - نعم، عادةً لا يفعل المنهي أي شيء على الإطلاق.لا يجب عليك تنفيذه إلا إذا كنت تتعامل مع موارد غير مُدارة.

وبعد ذلك، عندما تقوم بتنفيذه، فإنك تستخدمه نمط التخلص من مايكروسوفت (كما سبق وصفه)

  • public Dispose() المكالمات protected Dispose(true) - يتعامل مع كل من الموارد المدارة وغير المدارة.الاتصال Dispose() ينبغي قمع الانتهاء.

  • ~Finalize المكالمات protected Dispose(false) - يتعامل مع الموارد غير المُدارة فقط.يؤدي هذا إلى منع تسرب الذاكرة غير المُدارة إذا فشلت في الاتصال بـ public Dispose()

~Finalize إنه بطيء، ولا ينبغي استخدامه إلا إذا كان لديك موارد غير مُدارة للتعامل معها.

لا يمكن للموارد المُدارة أن تتسرب إلى الذاكرة، بل يمكنها فقط إهدار موارد التطبيق الحالي وإبطاء عملية تجميع البيانات المهملة الخاصة به.يمكن أن تتسرب الموارد غير المُدارة، و ~Finalize هي أفضل الممارسات للتأكد من عدم القيام بذلك.

في كلا الحالتين using هي أفضل الممارسات.

@ كيرت هاجنلوشر - هذا العودة إلى الأمام.ليس لدي أي فكرة عن سبب تصويت الكثيرين عليها عندما تكون خاطئة.

IDisposable هو ل تمكنت موارد.

النهائيات هي ل غير مُدار موارد.

طالما أنك تستخدم الموارد المُدارة فقط، فإن @Jon Limjap وأنا على صواب تمامًا.

بالنسبة للفصول التي تستخدم الموارد غير المُدارة (وضع في اعتبارك أن الغالبية العظمى من فئات .Net لا تفعل ذلك)، فإن إجابة باتريك شاملة وأفضل الممارسات.

تجنب استخدام GC.Collect - فهي طريقة بطيئة للتعامل مع الموارد المُدارة، ولا تفعل أي شيء مع الموارد غير المُدارة إلا إذا قمت ببناء ~Finalizers بشكل صحيح.


لقد قمت بإزالة تعليق المشرف من السؤال الأصلي بما يتماشى مع https://stackoverflow.com/questions/14593/etiquette-for-modifying-posts

ردًا على السؤال الأصلي، ومع المعلومات التي قدمها الملصق الأصلي حتى الآن، فمن المؤكد بنسبة 100% أنه لا يعرف ما يكفي عن البرمجة في .NET حتى يحصل على الإجابة:استخدم GC.Collect().أود أن أقول إنه من المحتمل بنسبة 99.99% أنه لا يحتاج حقًا إلى استخدام GC.Collect() على الإطلاق، كما أشارت معظم الملصقات.

تتلخص الإجابة الصحيحة في "دع GC يقوم بعمله".فترة.لديك أشياء أخرى تقلق بشأنها.ولكن قد ترغب في التفكير فيما إذا كان يجب عليك التخلص من كائنات معينة أو تنظيفها ومتى، وما إذا كنت بحاجة إلى تنفيذ IDisposable وربما Finalize في فصلك الدراسي.'

فيما يتعلق بمنشور كيث وقاعدته رقم 4:

بعض الملصقات تربك القاعدة 3 والقاعدة 4.قاعدة كيث رقم 4 صحيحة تمامًا ولا لبس فيها.إنها القاعدة الوحيدة من القواعد الأربعة التي لا تحتاج إلى تعديل على الإطلاق.أود أن أعيد صياغة بعض قواعده الأخرى قليلاً لجعلها أكثر وضوحًا، لكنها صحيحة بشكل أساسي إذا قمت بتحليلها بشكل صحيح، وقرأت المنشور بالكامل بالفعل لترى كيف يتوسع فيها.

  1. إذا كانت صفك لا يستخدم موردًا غير مُدار، كما أنه لا يقوم أبدًا بإنشاء كائن آخر من فئة يستخدم هو نفسه، بشكل مباشر أو في النهاية، كائنًا غير مُدار (أي فئة تنفذ IDisposable)، فلن تكون هناك حاجة لفصلك إما لتنفيذ IDisposable نفسه، أو حتى استدعاء .dispose على أي شيء.(في مثل هذه الحالة، من السخافة الاعتقاد بأنك تحتاج بالفعل إلى تحرير الذاكرة على الفور باستخدام GC قسري، على أي حال.)

  2. إذا كان فصلك يستخدم موردًا غير مُدار، أو يقوم بإنشاء كائن آخر يقوم بنفسه بتنفيذ IDisposable، فيجب على فصلك إما:

    أ) التخلص/الإفراج عنها فورًا في السياق المحلي الذي تم إنشاؤها فيه، أو...

    ب) قم بتنفيذ IDisposable بالنمط الموصى به في منشور Keith، أو في بضعة آلاف من الأماكن على الإنترنت، أو حرفيًا في حوالي 300 كتاب حتى الآن.

    ب.1) علاوة على ذلك، إذا كان (ب)، وهو مورد غير مُدار تم فتحه، فيجب دائمًا تنفيذ كل من IDisposable وFinalize، وفقًا لقاعدة Keith رقم 4.
    في هذا السياق، يعتبر Finalize بمثابة شبكة أمان بمعنى ما:إذا قام شخص ما بإنشاء كائن IDisposable الخاص بك والذي يستخدم موردًا غير مُدار، وفشل في استدعاء التخلص، فإن الإنهاء هو الفرصة الأخيرة لكائنك لإغلاق المورد غير المُدار بشكل صحيح.
    (يجب أن تقوم عملية الإنهاء بذلك عن طريق استدعاء Dispose بحيث تتخطى طريقة Dispose تحرير أي شيء باستثناء المورد غير المُدار.بدلاً من ذلك، إذا تم استدعاء أسلوب التخلص الخاص بالكائن الخاص بك بشكل صحيح من خلال أي شيء أنشأ مثيلاً للكائن الخاص بك، فإنه يقوم بتمرير استدعاء التخلص إلى جميع كائنات IDisposable التي قام بإنشاء مثيل لها، ويحرر الموارد غير المُدارة بشكل صحيح، وينتهي باستدعاء لمنع الإنهاء على الكائن الخاص بك ، مما يعني أن تأثير استخدام الإنهاء يقل إذا تم التخلص من العنصر الخاص بك بشكل صحيح بواسطة المتصل.تم تضمين كل هذه النقاط في مشاركة Keith، راجع للشغل.)

    ب.2) إذا كان فصلك يقوم فقط بتنفيذ IDisposable لأنه يحتاج بشكل أساسي إلى تمرير كائن Dispose إلى كائن IDisposable الذي قام بإنشاء مثيل له، فلا تقم بتنفيذ طريقة Finalize في فصلك في هذه الحالة.اللمسة النهائية مخصصة للتعامل مع الحالة التي لم يتم فيها استدعاء BOTH Dispose مطلقًا بواسطة أي كائن تم إنشاء مثيل له، وتم استخدام مورد غير مُدار لم يتم إصداره بعد.

باختصار، فيما يتعلق بمنشور كيث، فهو على حق تماما، وذلك المنشور هو الإجابة الأصح والأكمل في رأيي.قد يستخدم بعض العبارات المختصرة التي يجدها البعض "خاطئة" أو يعترض عليها، لكن منشوره الكامل يتوسع في استخدام Finalize بالكامل، وهو على حق تمامًا.تأكد من قراءة منشوره بالكامل قبل القفز على إحدى القواعد أو البيانات الأولية في منشوره.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top