سؤال

يجب عليك تعيين كافة الكائنات ل null (Nothing في VB.NET) بمجرد الانتهاء منهم؟

أدرك أنه في .NET من الضروري التخلص من أي مثيلات للكائنات التي تنفذ IDisposable واجهة لتحرير بعض الموارد على الرغم من أن الكائن لا يزال من الممكن أن يكون شيئًا بعد التخلص منه (وبالتالي isDisposed الملكية في النماذج)، لذا أفترض أنها لا تزال موجودة في الذاكرة أو على الأقل جزئيًا؟

أعلم أيضًا أنه عندما يخرج كائن ما عن النطاق، يتم وضع علامة عليه للتجميع ليكون جاهزًا للمرور التالي لمجمع البيانات المهملة (على الرغم من أن هذا قد يستغرق وقتًا).

لذلك مع وضع هذا في الاعتبار، سيتم ضبطه على null تسريع تحرير النظام للذاكرة لأنه لا يتعين عليه التأكد من أنها لم تعد في نطاقها وهل لها أي آثار جانبية سيئة؟

مقالات MSDN لا تفعل ذلك أبدًا في أمثلة ، وأنا أفعل ذلك حاليًا لأنني لا أستطيع رؤية الضرر.ومع ذلك فقد صادفت مزيجًا من الآراء لذا فإن أي تعليقات مفيدة.

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

المحلول

كارل صحيح تمامًا، ليست هناك حاجة لضبط الكائنات على قيمة خالية بعد الاستخدام.إذا تم تنفيذ كائن IDisposable, ، فقط تأكد من الاتصال IDisposable.Dispose() عند الانتهاء من هذا الكائن (ملفوفًا في ملف try..finally, ، أو، أ using() حاجز).ولكن حتى لو كنت لا تتذكر الاتصال Dispose(), ، يجب أن يتم استدعاء طريقة الإنهاء على الكائن Dispose() لك.

اعتقدت أن هذا كان علاجًا جيدًا:

حفر في IDisposable

وهذا

فهم معرف المتاح

ليس هناك أي فائدة من محاولة تخمين GC واستراتيجيات إدارتها لأنها ضبط ذاتي وغير شفافة.كانت هناك مناقشة جيدة حول الأعمال الداخلية مع جيفري ريختر في Dot Net Rocks هنا: جيفري ريختر على نموذج ذاكرة Windows و Richters كتاب CLR عبر C# الفصل 20 له علاج عظيم:

نصائح أخرى

سبب آخر لتجنب تعيين الكائنات على قيمة فارغة عند الانتهاء منها هو أنه يمكن أن يبقيها على قيد الحياة لفترة أطول.

على سبيل المثال

void foo()
{
    var someType = new SomeType();
    someType.DoSomething();
    // someType is now eligible for garbage collection         

    // ... rest of method not using 'someType' ...
}

سيسمح للكائن المشار إليه بواسطة someType بأن يكون GC'd بعد استدعاء "DoSomething" ولكن

void foo()
{
    var someType = new SomeType();
    someType.DoSomething();
    // someType is NOT eligible for garbage collection yet
    // because that variable is used at the end of the method         

    // ... rest of method not using 'someType' ...
    someType = null;
}

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

لا لا كائنات فارغة.يمكنك التحقق من http://codebetter.com/blogs/karlseguin/archive/2008/04/27/foundations-of-programming-pt-7-back-to-basics-memory.aspx لمزيد من المعلومات، ولكن تعيين الأشياء على قيمة خالية لن يفعل أي شيء، باستثناء تشويه التعليمات البرمجية الخاصة بك.

أيضًا:

using(SomeObject object = new SomeObject()) 
{
  // do stuff with the object
}
// the object will be disposed of

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

إذا قام كائن بتطبيق IDisposable وتم تخزينه في حقل، فأعتقد أنه من الجيد إلغاؤه، فقط لتجنب استخدام الكائن الذي تم التخلص منه.الأخطاء من النوع التالي يمكن أن تكون مؤلمة:

this.myField.Dispose();
// ... at some later time
this.myField.DoSomething();

من الجيد إلغاء الحقل بعد التخلص منه، والحصول على NullPtrEx مباشرةً عند السطر الذي يتم فيه استخدام الحقل مرة أخرى.وإلا، فقد تواجه بعض الأخطاء الغامضة في المستقبل (اعتمادًا على ما يفعله DoSomething بالضبط).

من المحتمل أن التعليمات البرمجية الخاصة بك ليست منظمة بشكل محكم بما فيه الكفاية إذا كنت تشعر بالحاجة إلى ذلك null المتغيرات.

هناك عدة طرق لتحديد نطاق المتغير:

كما ذكر ستيف ترانبي

using(SomeObject object = new SomeObject()) 
{
  // do stuff with the object
}
// the object will be disposed of

وبالمثل، يمكنك ببساطة استخدام الأقواس المتعرجة:

{
    // Declare the variable and use it
    SomeObject object = new SomeObject()
}
// The variable is no longer available

أجد أن استخدام الأقواس المتعرجة بدون أي "عنوان" لتنظيف الكود والمساعدة في جعله أكثر قابلية للفهم.

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

بشكل عام لا حاجة لتعيينها إلى قيمة فارغة.ولكن لنفترض أن لديك وظيفة إعادة التعيين في صفك.

ثم قد تفعل ذلك، لأنك لا تريد استدعاء التخلص مرتين، نظرًا لأن بعض عمليات التخلص قد لا يتم تنفيذها بشكل صحيح ويتم طرح استثناء System.ObjectDispose.

private void Reset()
{
    if(_dataset != null)
    {
       _dataset.Dispose();
       _dataset = null;
    }
    //..More such member variables like oracle connection etc. _oraConnection
 }

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

نعم، يجب عليك الاتصال دائما .Dispose() أو .Close() على أي شيء يحتوي عليه عند الانتهاء.سواء كان ذلك مقابض الملفات أو اتصالات قاعدة البيانات أو الكائنات التي يمكن التخلص منها.

منفصل عن ذلك هو النمط العملي جدًا لـ LazyLoad.

أقول لقد وأنشأت مثيلا ObjA ل class A. Class A لديه ملكية عامة تسمى PropB ل class B.

داخليا، PropB يستخدم المتغير الخاص ل _B والإعدادات الافتراضية فارغة.متى PropB.Get() يتم استخدامه، فإنه يتحقق لمعرفة ما إذا كان _PropB يكون فارغًا، وإذا كان كذلك، فإنه يفتح الموارد اللازمة لإنشاء مثيل لـ B داخل _PropB.ثم يعود _PropB.

من تجربتي، هذه خدعة مفيدة حقًا.

حيث تأتي الحاجة إلى القيمة الخالية إذا قمت بإعادة تعيين أو تغيير A بطريقة ما بحيث لا تكون محتوياته _PropB كانوا أبناء القيم السابقة لـ A, ، سوف تحتاج إلى التخلص منها وإلغائها _PropB حتى يتمكن LazyLoad من إعادة التعيين لجلب القيمة الصحيحة إذا كان الرمز يتطلب ذلك.

إذا كنت تفعل ذلك فقط _PropB.Dispose() وبعد فترة وجيزة من توقع نجاح عملية التحقق الفارغة من LazyLoad، لن تكون فارغة، وستنظر إلى البيانات القديمة.في الواقع، يجب عليك إلغاؤه بعد ذلك Dispose() فقط للتأكد.

أتمنى بالتأكيد أن يكون الأمر خلاف ذلك، ولكن لدي كود يعرض الآن هذا السلوك بعد فترة Dispose() على _PropB وخارج وظيفة الاستدعاء التي قامت بالتخلص (وبالتالي خارج النطاق تقريبًا)، فإن الدعامة الخاصة لا تزال غير فارغة، ولا تزال البيانات القديمة موجودة.

في نهاية المطاف، سوف تصبح الخاصية التي تم التخلص منها لاغية، ولكن هذا لم يكن حتميًا من وجهة نظري.

السبب الأساسي، كما يشير dbkk هو أن الحاوية الأصلية (ObjA مع PropB) هو الاحتفاظ بمثيل _PropB في نطاقه، على الرغم من Dispose().

هناك بعض الحالات حيث يكون من المنطقي استخدام مراجع فارغة.على سبيل المثال، عندما تكتب مجموعة - مثل قائمة انتظار الأولوية - وبموجب عقدك، لا ينبغي لك الاحتفاظ بهذه الكائنات حية للعميل بعد أن يقوم العميل بإزالتها من قائمة الانتظار.

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

على العموم، لا ينبغي عليك أن تهتم حقًا.اسمح للمترجم وGC بالقيام بعملهما حتى تتمكن أنت من القيام بعملك.

ألقِ نظرة على هذه المقالة أيضًا: http://www.codeproject.com/KB/cs/idisposable.aspx

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

يشرح ستيفن كليري جيدًا في هذا المنشور: هل يجب علي تعيين المتغيرات إلى Null للمساعدة في جمع البيانات المهملة؟

يقول:

الإجابة المختصرة ، لنعم الصبر ، إذا كان المتغير هو حقل ثابت ، أو إذا كنت تكتب طريقة قابلة للتعداد (باستخدام عودة العائد) أو طريقة غير متزامنة (باستخدام Async وانتظر).بخلاف ذلك لا.

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

(حتى إذا كنت تقوم بتطبيق IDisposable.Dispose، فلا يزال يتعين عليك عدم تعيين المتغيرات على القيمة null).

الشيء المهم الذي يجب أن نأخذه بعين الاعتبار هو الحقول الثابتة.

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

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

خاتمة:

الحقول الثابتة;هذا كل ما في الأمر.أي شيء آخر هو أ مضيعة للوقت.

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

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

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

أعتقد أن هذا يجعل نيتي أكثر وضوحًا للأحمق المسكين التالي الذي يتبع خطاي، وإذا كان الأمر كذلك "قد" من المحتمل أن أساعد GC في بعض الأحيان، فالأمر يستحق ذلك بالنسبة لي.غالبًا ما يجعلني أشعر بالترتيب والوضوح، ويحب مونجو أن يشعر بالترتيب والوضوح.:)

ما اراه هو ان مثل هذا:توجد لغات البرمجة للسماح للأشخاص بإعطاء الآخرين فكرة عن النوايا والمترجم طلب عمل حول ما يجب القيام به - يقوم المترجم بتحويل هذا الطلب إلى لغة مختلفة (أحيانًا عدة) لوحدة المعالجة المركزية - يمكن أن توفر وحدة المعالجة المركزية (وحدات المعالجة المركزية) تعرف على اللغة التي استخدمتها، وإعدادات علامة التبويب، والتعليقات، والتأكيدات الأسلوبية، وأسماء المتغيرات، وما إلى ذلك.- تدور وحدة المعالجة المركزية حول تدفق البتات الذي يخبرها بالسجلات وأكواد التشغيل ومواقع الذاكرة التي يجب تغييرها.لا يتم تحويل العديد من الأشياء المكتوبة في التعليمات البرمجية إلى ما تستهلكه وحدة المعالجة المركزية بالتسلسل الذي حددناه.لدينا C، C++، C#، Lisp، Babel، المجمع أو أي شيء هو نظرية وليس حقيقة، مكتوب كبيان عمل.ما تراه ليس هو ما تحصل عليه، نعم، حتى في لغة المجمع.

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

هناك استثناءات لأي قاعدة.في السيناريوهات ذات الذاكرة المتطايرة، والذاكرة الثابتة، وظروف السباق، والمفردات، واستخدام البيانات "التي لا معنى لها" وكل هذا النوع من التعفن، يكون الأمر مختلفًا:أنت بحاجة إلى إدارة ذاكرتك الخاصة، وقفلها وإبطالها لأن الذاكرة ليست جزءًا من عالم GC'd - ونأمل أن يفهم الجميع ذلك.أما بقية الوقت مع لغات GC، فهي مسألة أسلوب وليست ضرورة أو تعزيزًا مضمونًا للأداء.

في نهاية اليوم، تأكد من أنك تفهم ما هو مؤهل للحصول على GC وما هو غير مؤهل؛القفل والتخلص والإبطال بشكل مناسب؛وضع الشمع ازالة الشمع؛شهيق زفير؛ولكل شيء آخر أقول:إذا كان هذا سيشعرك بالارتياح، إفعله.قد تختلف المسافة المقطوعة... كما ينبغي...

بعض الأشياء تفترض أن .dispose() الطريقة التي تفرض إزالة المورد من الذاكرة.

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