簡體   English   中英

在 c# 中訂閱 (+=) 和取消訂閱 (-=) 委托的復雜性是多少?

[英]What is the complexity of subscribing (+=) and unsubscribing (-=) a delegate in c#?

在 c# 中訂閱 (+=) 和取消訂閱 (-=) 委托的復雜性是多少?

namespace MulticastDelegateDemo
{
    public delegate void MathDelegate(int No1, int No2);

    public class Program
    {
        public static void Add(int x, int y)
        {
            Console.WriteLine("THE SUM IS : " + (x + y));
        }
        public static void Sub(int x, int y)
        {
            Console.WriteLine("THE SUB IS : " + (x - y));
        }
        public void Mul(int x, int y)
        {
            Console.WriteLine("THE MUL IS : " + (x * y));
        }
        public void Div(int x, int y)
        {
            Console.WriteLine("THE DIV IS : " + (x / y));
        }
        
        static void Main(string[] args)
        {
            Program p = new Program();
            MathDelegate del1 = new MathDelegate(Add);
            MathDelegate del2 = new MathDelegate(Program.Sub);
            MathDelegate del3 = p.Mul;
            MathDelegate del4 = new MathDelegate(p.Div); ;

            //In this example del5 is a multicast delegate. We can use +(plus) 
            // operator to chain delegates together and -(minus) operator to remove.
            MathDelegate del5 = del1 + del2 + del3 + del4;
            del5.Invoke(20, 5);
            Console.WriteLine();
            del5 -= del2;
            del5(22, 7);
            
            Console.ReadKey();
        }
    }
}

實現將取決於編譯器; 然而,基於接口、限制和用例,使用鏈表實現后端將是最簡單且最有可能最有效的。 主要影響將是取消訂閱查找。

您可以在 Visual Studio 19 中多次訂閱同一個委托(如果您泄漏數十次,性能會受到巨大影響),因此實現顯然只是附加您提供的內容。

做一個簡單的循環測試訂閱明顯更快,取消訂閱正在我的筆記本電腦上踢風扇

這是使用 StopWatch class 和 ElapsedMilliseconds - 在標准事件聲明和方法之外,這些類是空的。

Looping 50000 times
Subscribe: 15
Unsubscribe: 9202
        static void Main(string[] args)
        {
            EventSubscirber ms = new EventSubscirber();
            MyEventClass myEventClass = new MyEventClass();

            int loops = 50000;

            Stopwatch swsubscribe = new Stopwatch();
            Stopwatch swunsubscribe = new Stopwatch();

            swsubscribe.Start();
            for (int i = 0; i < loops; i++)
            {
                myEventClass.SampleEvent += ms.SampleEventReceiver;
            }
            swsubscribe.Stop();
            Console.WriteLine($"Looping {loops} times");
            Console.WriteLine($"Subscribe: {swsubscribe.ElapsedMilliseconds}");

            swunsubscribe.Start();
            for (int i = 0; i < loops; i++)
            {
                myEventClass.SampleEvent -= ms.SampleEventReceiver;
            }

            swunsubscribe.Stop();
            Console.WriteLine($"Unsubscribe: {swunsubscribe.ElapsedMilliseconds}");
        }

只是猜測,但基於時間,它每次迭代完整列表並取消訂閱最后一個匹配的列表。

訂閱 ( += ) 和取消訂閱 ( -= ) 實際上是System.Delegate.CombineSystem.Delegate.Remove static 方法調用的簡寫。 它們在內部為傳遞的委托對相應地調用CombineImplRemoveImpl (對於傳遞了 null 參數的情況有一些排除)。

您可以在source.dot.net上自己檢查實際實現。 在那里,您可以看到MulticastDelegate.CombineImpl的復雜度為O(m) ,其中m是右操作數調用列表中的項目數:

Action act = null; int count = 25000; var sw = new Stopwatch();

sw.Restart();
for (int i = 0; i < count; i++)
{
    Action a = () => Console.WriteLine(); 
    act = (a += act);
}
Console.WriteLine($"Subscribe - {count}: {sw.ElapsedMilliseconds}"); // prints "Subscribe - 25000: 1662" on my machine

sw.Restart();
for (int i = 0; i < count; i++)
{
     act += () => Console.WriteLine();
}
Console.WriteLine($"Subscribe - {count}: {sw.ElapsedMilliseconds}"); // prints "Subscribe - 25000: 2" on my machine

至於MulticastDelegate.RemoveImpl ,很難快速估計復雜性,但對於n > m的情況,它似乎是O(n) (其中n是右操作數調用列表中的項目數)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM