[英]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.Combine
和System.Delegate.Remove
static 方法調用的簡寫。 它們在內部為傳遞的委托對相應地調用CombineImpl
和RemoveImpl
(對於傳遞了 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.