سؤال

لقد حصلت على فئة C# مع Dispose وظيفة عبر IDisposable.الغرض منه هو استخدامه داخل أ using حظره حتى يمكن تحرير المورد الباهظ الثمن الذي يتعامل معه على الفور.

تكمن المشكلة في حدوث خطأ عندما تم طرح استثناء من قبل Dispose تم استدعاؤه، وأهمل المبرمج استخدامه using أو finally.

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

هل هناك أي طريقة ل using كتلة ليتم استخدامها تلقائيا في C#؟

تشكرات.

تحديث:

أود أن أشرح سبب عدم قبولي للإجابات النهائية.هذه الإجابات صحيحة من الناحية الفنية في حد ذاتها، لكنها ليست مدمرة لأسلوب C++.

هذا هو الخطأ الذي وجدته، وقد تم اختصاره إلى الأساسيات...

try
{
    PleaseDisposeMe a = new PleaseDisposeMe();
    throw new Exception();
    a.Dispose();
}
catch (Exception ex)
{
    Log(ex);
}

// This next call will throw a time-out exception unless the GC
// runs a.Dispose in time.
PleaseDisposeMe b = new PleaseDisposeMe();

استخدام FXCop هو اقتراح ممتاز، ولكن إذا كانت هذه هي إجابتي الوحيدة، فيجب أن يتحول سؤالي إلى نداء إلى الأشخاص الذين يستخدمون C#، أو يستخدمون C++.عشرين متداخلة باستخدام البيانات أي شخص؟

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

المحلول

لسوء الحظ لا توجد طريقة للقيام بذلك مباشرة في الكود.إذا كانت هذه مشكلة داخلية، فهناك العديد من حلول تحليل التعليمات البرمجية التي يمكنها اكتشاف هذا النوع من المشكلات.هل بحثت في FxCop؟أعتقد أن هذا سيكشف هذه المواقف وفي جميع الحالات التي قد يتم فيها ترك كائنات IDisposable معلقة.إذا كان مكونًا يستخدمه الأشخاص خارج مؤسستك ولا يمكنك طلب FxCop، فإن التوثيق هو حقًا ملاذك الوحيد :).

يحرر:في حالة اللمسات النهائية، لا يضمن هذا حقًا موعد حدوث اللمسات النهائية.لذلك قد يكون هذا حلاً لك ولكنه يعتمد على الموقف.

نصائح أخرى

حيث أعمل نستخدم الإرشادات التالية:

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

لجعل حياتنا أسهل، لدينا أيضًا طريقة SafeDispose في بنيتنا التحتية، والتي تستدعي طريقة Dispose للوسيطة الخاصة بها داخل كتلة محاولة الالتقاط (مع تسجيل الأخطاء)، فقط في حالة (على الرغم من أنه ليس من المفترض أن تقوم أساليب التخلص بالاستثناءات ).

أنظر أيضا: كريس ليوناقتراحات بخصوص IDisposable

يحرر:@مشاكس:شيء واحد يجب عليك فعله هو استدعاء GC.SuppressFinalize داخل "التخلص"، بحيث إذا تم التخلص من الكائن، فلن يتم "إعادة التخلص منه".

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

class MyDisposable: IDisposable {
    public void Dispose() {
        lock(this) {
            if (disposed) {
                return;
            }

            disposed = true;
        }

        GC.SuppressFinalize(this);

        // Do actual disposing here ...
    }

    private bool disposed = false;
}

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

@مشاكس

إذا سيتم استدعاؤه عندما يتم نقل الكائن خارج النطاق ويتم ترتيبه بواسطة أداة تجميع البيانات المهملة.

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

في الواقع، منحت شركة Microsoft منحة إلى Chris Sells لإنشاء تطبيق لـ .NET يستخدم العد المرجعي بدلاً من تجميع البيانات المهملة وصلة.كما تبين أنه كان هناك ضخم ضرب الأداء.

~ClassName()
{
}

تحرير (غامق):

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

نمط التخلص "المقبول" وفقًا لإرشادات الإطار هو كما يلي مع الموارد غير المُدارة:

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

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // tidy managed resources
            }

            // tidy unmanaged resources
        }
    }

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

أفضل الممارسات هي استخدام أداة الإنهاء في فصلك الدراسي واستخدامها دائمًا using كتل.

لا يوجد حقًا مكافئ مباشر، حيث تبدو أدوات الإنهاء النهائية مثل أدوات تدمير لغة C، ولكنها تتصرف بشكل مختلف.

من المفترض أن تعشش using الكتل، ولهذا السبب يكون تخطيط كود C# افتراضيًا لوضعها على نفس السطر ...

using (SqlConnection con = new SqlConnection("DB con str") )
using (SqlCommand com = new SqlCommand( con, "sql query") )
{
    //now code is indented one level
    //technically we're nested twice
}

عندما لا تستخدم using يمكنك فقط أن تفعل ما تفعله تحت الغطاء على أي حال:

PleaseDisposeMe a;
try
{
    a = new PleaseDisposeMe();
    throw new Exception();
}
catch (Exception ex) { Log(ex); }  
finally {    
    //this always executes, even with the exception
    a.Dispose(); 
}

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

وهذا لا يختلف عن نسيان المبرمج للاستخدام يمسح في C++، إلا أنه على الأقل هنا سيظل جامع البيانات المهملة يلحق به في النهاية.

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

التصميم الأفضل هو جعل هذه الفئة تطلق المورد الباهظ الثمن بنفسها، قبل التخلص منه.

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

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