سؤال

لدي فئة مجردة تنفذ عناية، مثل ذلك:

public abstract class ConnectionAccessor : IDisposable
{
    public abstract void Dispose();
}

في نظام فريق Visual Studio 2008، رضع تحليل الكود في مشروعي وكان أحد التحذيرات التي جاءت ما يلي:

Microsoft.Design: تعديل "ConnectionAccessor.Dispose ()"، بحيث يتم تخلص منها (صحيح)، ثم يستدعي gc.suppressFinalize على مثيل الكائن الحالي ("هذا" أو "أنا" في Visual Basic)، ثم إرجاع.

هل هي مجرد سخيفة، أخبرني بتعديل جسم طريقة مجردة، أو يجب أن أفعل شيئا ما في أي مثيل مشتق من Dispose?

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

المحلول

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

public class Base
{
    ~Base()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // so that Dispose(false) isn't called later
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
             // Dispose all owned managed objects
        }

        // Release unmanaged resources
    }
}

المفتاح هنا هو أنه لا يوجد تكرار بين النهائي و Dispose لتنظيف غير مدار، ومع ذلك، يمكن لأي فئة مشتقة تمديد كل من التنظيف المدار وغير المداري.

لحالتك، ما يجب عليك فعله هو:

protected abstract void Dispose(bool disposing)

وترك كل شيء آخر كما هو. حتى أنه من القيمة المشكوك فيها، لأنك تطبق فئاتك المشتقة في التنفيذ Dispose الآن - وكيف تعرف أن جميعهم بحاجة إليها؟ إذا لم يكن من فئتك الأساسية لا يوجد شيء لتخلصه، بل من المحتمل أن تفعل معظم الفئات المشتقة (مع استثناءات قليلة، ربما)، فما عليك سوى تقديم تطبيق فارغ. وهو ما System.IO.Stream (نفسه مجردة) يفعل، لذلك هناك سابقة.

نصائح أخرى

تحذير يخبرك أساسا أن تنفذ التخلص من نمط في صفك.

يجب أن يبدو الكود الناتج هكذا:

public abstract class ConnectionAccessor : IDisposable
{
    ~ConnectionAccessor()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
    }
}

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

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

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

يمكنني استخدام نمط التخلص المذكور في كتاب بروس فاغنز فعالة ج # وهو أساسا:

public class BaseClass : IDisposable
{
    private bool _disposed = false;
    ~BaseClass()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        _disposed = true;
    }
}

public void Derived : BaseClass
{
    private bool _disposed = false;

    protected override void Dispose(bool disposing)
    {
        if (_disposed) 
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        base.Dispose(disposing);
        _disposed = true;
    }

التحذير مثير للاهتمام رغم ذلك. Lippert ERIC، أحد المصممين C #، مدونين حول سبب وجود رسائل الخطأ "التشخيص ولكن ليس إلزامية: صف المشكلة، وليس الحل".قرأت هنا.

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