هل يمكن أن يؤدي استخدام lambdas كمعالجات للأحداث إلى حدوث تسرب للذاكرة؟

StackOverflow https://stackoverflow.com/questions/16473

سؤال

لنفترض أن لدينا الطريقة التالية:

private MyObject foo = new MyObject();

// and later in the class

public void PotentialMemoryLeaker(){
  int firedCount = 0;
  foo.AnEvent += (o,e) => { firedCount++;Console.Write(firedCount);};
  foo.MethodThatFiresAnEvent();
}

إذا تم إنشاء مثيل للفئة بهذه الطريقة و PotentialMemoryLeaker يتم استدعاء الطريقة عدة مرات، هل نقوم بتسريب الذاكرة؟

هل هناك أي طريقة لإلغاء ربط معالج أحداث لامدا بعد أن ننتهي من الاتصال MethodThatFiresAnEvent?

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

المحلول

نعم، احفظه في متغير وقم بإزالته.

DelegateType evt = (o, e) => { firedCount++; Console.Write(firedCount); };
foo.AnEvent += evt;
foo.MethodThatFiresAnEvent();
foo.AnEvent -= evt;

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

نصائح أخرى

لن تقوم فقط بتسريب الذاكرة، بل سيتم أيضًا استدعاء لامدا عدة مرات.سيضيف كل استدعاء لـ "PotentialMemoryLeaker" نسخة أخرى من lambda إلى قائمة الأحداث، وسيتم استدعاء كل نسخة عند تشغيل "AnEvent".

حسنًا، يمكنك تمديد ما تم إنجازه هنا لجعل استخدام المفوضين أكثر أمانًا (لا يوجد تسرب للذاكرة)

يتم تجميع مثالك فقط إلى فئة داخلية خاصة باسم المترجم (مع الحقلfireCount وطريقة تحمل اسم المترجم).ينشئ كل استدعاء لـ PotentialMemoryLeaker مثيلًا جديدًا لفئة الإغلاق حيث يحتفظ foo بمرجع عن طريق تفويض للأسلوب الفردي.

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

foreach (var handler in AnEvent.GetInvocationList()) AnEvent -= handler;

وبطبيعة الحال، سوف تحتاج إلى الوصول إلى MyObject أعضاء الفصل الخاصين.

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

someobject.SomeEvent += () => ...;
someobject.SomeEvent += delegate () {
    ...
};

// unhook
Action del = () => ...;
someobject.SomeEvent += del;
someobject.SomeEvent -= del;

لذا فهي في الأساس مجرد يد مختصرة لما كنا نستخدمه في 2.0 طوال هذه السنوات.

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