在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();
}
}
}发布于 2020-08-03 05:07:30
订阅(+=)和取消订阅(-=)实际上是System.Delegate.Combine和System.Delegate.Remove静态方法调用的简写。对于传递的委托对,它们在内部调用相应的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是正确操作数调用列表中的项数)。
发布于 2020-07-27 20:39:12
实现将依赖于编译器;但是,根据接口、限制和用例,使用链表实现后端将是最简单和最有效的。主要的影响将是取消订阅查找。
你可以在Visual Studio19中多次订阅同一个委托(如果你泄露了几十次,就会对性能造成巨大的影响),所以实现显然只是附加了你给它的东西。
做一个简单的循环测试,订阅的速度要快得多,而取消订阅则会让我的笔记本电脑上的风扇着迷
这使用了StopWatch类和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}");
}只是猜测,但基于时间,它每次迭代完整的列表,并取消订阅最后一个匹配的列表。
https://stackoverflow.com/questions/63114658
复制相似问题