هل هناك جانب سلبي مضيفا مجهول فارغة مندوب على الحدث الإعلان ؟

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

سؤال

لقد رأيت بضعة يذكر هذا المصطلح (بما في ذلك على ذلك):

// Deliberately empty subscriber
public event EventHandler AskQuestion = delegate {};

الاتجاه الصعودي هو واضح - أنه يتجنب الحاجة إلى التحقق null قبل رفع الحدث.

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

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

المحلول

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

نصائح أخرى

بدلا من حمل أداء النفقات العامة ، لماذا لا استخدام طريقة تمديد للتخفيف من المشاكل على حد سواء:

public static void Raise(this EventHandler handler, object sender, EventArgs e)
{
    if(handler != null)
    {
        handler(sender, e);
    }
}

مرة واحدة محددة, أنك لن تضطر إلى فعل آخر null الحدث التحقق مرة أخرى:

// Works, even for null events.
MyButtonClick.Raise(this, EventArgs.Empty);

للأنظمة التي يجعل الاستخدام المكثف من الأحداث الأداء الحرجة, سوف بالتأكيد نريد على الأقل النظر في لا تفعل هذا.تكلفة رفع الحدث مع فارغة المندوب هو ما يقرب من ضعف ذلك من أجل رفع ذلك مع null تحقق أولا.

وهنا بعض الأرقام تشغيل المعايير على الجهاز الخاص بي:

For 50000000 iterations . . .
No null check (empty delegate attached): 530ms
With null check (no delegates attached): 249ms
With null check (with delegate attached): 452ms

و هنا هو رمز اعتدت على هذه الأرقام:

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        public event EventHandler<EventArgs> EventWithDelegate = delegate { };
        public event EventHandler<EventArgs> EventWithoutDelegate;

        static void Main(string[] args)
        {
            //warm up
            new Program().DoTimings(false);
            //do it for real
            new Program().DoTimings(true);

            Console.WriteLine("Done");
            Console.ReadKey();
        }

        private void DoTimings(bool output)
        {
            const int iterations = 50000000;

            if (output)
            {
                Console.WriteLine("For {0} iterations . . .", iterations);
            }

            //with anonymous delegate attached to avoid null checks
            var stopWatch = Stopwatch.StartNew();

            for (var i = 0; i < iterations; ++i)
            {
                RaiseWithAnonDelegate();
            }

            stopWatch.Stop();

            if (output)
            {
                Console.WriteLine("No null check (empty delegate attached): {0}ms", stopWatch.ElapsedMilliseconds);
            }


            //without any delegates attached (null check required)
            stopWatch = Stopwatch.StartNew();

            for (var i = 0; i < iterations; ++i)
            {
                RaiseWithoutAnonDelegate();
            }

            stopWatch.Stop();

            if (output)
            {
                Console.WriteLine("With null check (no delegates attached): {0}ms", stopWatch.ElapsedMilliseconds);
            }


            //attach delegate
            EventWithoutDelegate += delegate { };


            //with delegate attached (null check still performed)
            stopWatch = Stopwatch.StartNew();

            for (var i = 0; i < iterations; ++i)
            {
                RaiseWithoutAnonDelegate();
            }

            stopWatch.Stop();

            if (output)
            {
                Console.WriteLine("With null check (with delegate attached): {0}ms", stopWatch.ElapsedMilliseconds);
            }
        }

        private void RaiseWithAnonDelegate()
        {
            EventWithDelegate(this, EventArgs.Empty);
        }

        private void RaiseWithoutAnonDelegate()
        {
            var handler = EventWithoutDelegate;

            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }
}

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

لكل مثيل الميدانية في كل فئة سوف لا تزال تأخذ نفس المساحة بالطبع.

أي

internal static class Foo
{
    internal static readonly EventHandler EmptyEvent = delegate { };
}
public class Bar
{
    public event EventHandler SomeEvent = Foo.EmptyEvent;
}

بخلاف ذلك ، يبدو على ما يرام.

أفهم أن الفارغة مندوب الموضوع آمنة ، بينما null الاختيار غير.

وأود أن أقول انها قليلا من خطورة بناء, لأنه يغري لك أن تفعل شيئا مثل :

MyEvent(this, EventArgs.Empty);

إذا كان العميل يطرح استثناء الخادم يذهب معها.

ثم ربما يمكنك القيام به:

try
{
  MyEvent(this, EventArgs.Empty);
}
catch
{
}

ولكن إذا كان لديك العديد من المشتركين مشترك واحد يطرح استثناء ، ماذا يحدث أخرى مشتركين ؟

ولتحقيق ذلك ، لقد تم استخدام بعض ساكنة مساعد الأساليب التي تفعل null تحقق يبتلع أي استثناء من المشترك الجانب (هذا من idesign).

// Usage
EventHelper.Fire(MyEvent, this, EventArgs.Empty);


public static void Fire(EventHandler del, object sender, EventArgs e)
{
    UnsafeFire(del, sender, e);
}
private static void UnsafeFire(Delegate del, params object[] args)
{
    if (del == null)
    {
        return;
    }
    Delegate[] delegates = del.GetInvocationList();

    foreach (Delegate sink in delegates)
    {
        try
        {
            sink.DynamicInvoke(args);
        }
        catch
        { }
    }
}

لا يوجد معنى الأداء عقوبة للحديث عن, إلا, وربما في بعض الحالات القصوى.

ولكن لاحظ أن هذه الخدعة يصبح أقل أهمية في C# 6.0, لأن اللغة توفر بديلا الجملة إلى استدعاء المندوبين التي قد تكون فارغة:

delegateThatCouldBeNull?.Invoke(this, value);

أعلاه, null المشروط المشغل ?. يجمع بين null التحقق مع المشروط الاحتجاج.

بدلا من "فارغة مندوب" نهج واحد يمكن تعريف بسيط امتداد طريقة لتغليف الطريقة التقليدية من فحص حالة معالج ضد null.هو موضح هنا و هنا.

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

public class X
{
    public delegate void MyDelegate();
    public MyDelegate MyFunnyCallback = delegate() { }

    public void DoSomething()
    {
        MyFunnyCallback();
    }
}


X x = new X();

x.MyFunnyCallback = delegate() { Console.WriteLine("Howdie"); }

x.DoSomething(); // works fine

// .. re-init x
x.MyFunnyCallback = null;

// .. continue
x.DoSomething(); // crashes with an exception

الشيء هو:أنت لا تعرف أبدا الذين سيتم استخدام التعليمات البرمجية الخاصة بك في أي شكل من الأشكال.أنت لا تعرف أبدا إذا كان في بعض السنوات خلال إصلاح الخلل من التعليمات البرمجية الحدث/معالج يتم تعيين إلى null.

دائما كتابة إذا تحقق.

على أمل أن يساعد ;)

ps:شكرا على حساب الأداء.

pps:تحريره من الحدث الحالة إلى رد سبيل المثال.شكرا على ملاحظاتك ...أنا "مشفرة" على سبيل المثال w/o Visual Studio و تعديل المثال كان في ذهني أن هذا الحدث.آسف على الخلط.

الشراكات بين القطاعين العام والخاص:لا أعرف إذا كان لا يزال يناسب الموضوع ...ولكن أعتقد أنه هو مبدأ هام.يرجى التحقق أيضا موضوع آخر من stackflow

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