1.如果多次注册同一个事件处理函数时,触发时处理函数是否也会多次触发?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestCSharp
{
class Animation
{
public delegate void PlayHandler();
public event PlayHandler Callback;
public Animation()
{
this.Callback += Move;
this.Callback += Move;
this.Callback += Move;
this.Callback();
}
public void Move()
{
Console.WriteLine("Move");
}
public void Run()
{
Console.WriteLine("Run");
}
public void Jump()
{
Console.WriteLine("Jump");
}
}
}
可以注册多次同一个函数,触发时会如上图所示执行多次.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestCSharp
{
class Animation
{
public delegate void PlayHandler();
public event PlayHandler Callback;
public Animation()
{
this.Callback += Move;
this.Callback -= Move;
this.Callback -= Move;
this.Callback();
}
public void Move()
{
Console.WriteLine("Move");
}
public void Run()
{
Console.WriteLine("Run");
}
public void Jump()
{
Console.WriteLine("Jump");
}
}
}
注册一次,可以注销多次,但是要注意触发时要检查是否为空,做如下修改后避免如上图的空指针异常
if (this.Callback != null)
{
this.Callback();
}
3.如何认定两个事件处理函数是一样的? 如果是匿名函数呢?
using System.Text;
namespace TestCSharp
{
class Animation
{
public delegate void PlayHandler();
public event PlayHandler Callback;
public Animation()
{
this.Callback += ()=> Console.WriteLine("Jump");
//Event Unsubscription Via Anonymous Delegate
this.Callback -= () => Console.WriteLine("Jump");
}
}
}
Resharper给出提示Event Unsubscription Via Anonymous Delegate,看起来一致的匿名函数,实际上方法签名是不一样,所以不能如上代码所示取消订阅一个匿名方法。如下代码给出如何取消订阅匿名方法的解决方案。
using System.Text;
namespace TestCSharp
{
class Animation
{
public delegate void PlayHandler();
public event PlayHandler Callback;
public Animation()
{
PlayHandler func = () => Console.WriteLine("Jump");
this.Callback += func;
this.Callback -= func;
this.Callback += func;
this.Callback();
}
}
}
更多取消匿名事件的资料参见官方文档:http://msdn.microsoft.com/en-us/library/ms366768.aspx
4.如果不手动注销事件函数,系统会帮我们回收吗?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestCSharp
{
class EventSource
{
public delegate void PlayHandler();
public event PlayHandler Callback;
public void Excute()
{
if (this.Callback != null)
{
this.Callback();
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestCSharp
{
class Listener
{
public Listener(EventSource source)
{
source.Callback += Run;
}
private void Run()
{
Console.WriteLine("Run");
}
~Listener()
{
Console.WriteLine("Listener 销毁");
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
namespace TestCSharp
{
class MainClass
{
public static void Main(string[] args)
{
EventSource s = new EventSource();
Listener l = new Listener(s);
s.Excute();
l = null;
Gc();
s.Excute();
s = null;
Gc();
}
public static void Gc()
{
Console.WriteLine("GC 开始");
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("GC 结束");
}
}
}
注意第二次调用s.Excute()前,已将listener赋为null,并执行Gc,但是仍然输出Run。直到将source赋为null时,listener才被回收掉。
下面我们手工注销事件,看看系统能不能自动回收?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestCSharp
{
class Listener
{
public Listener(EventSource source)
{
source.Callback += Run;
}
//添加一个手工注销方法
public void DeregisterEvent(EventSource source)
{
source.Callback -= Run;
}
private void Run()
{
Console.WriteLine("Run");
}
~Listener()
{
Console.WriteLine("Listener 销毁");
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
namespace TestCSharp
{
class MainClass
{
public static void Main(string[] args)
{
EventSource s = new EventSource();
Listener l = new Listener(s);
s.Excute();
//手工调用注销
l.DeregisterEvent(s);
l = null;
Gc();
s.Excute();
s = null;
Gc();
}
public static void Gc()
{
Console.WriteLine("GC 开始");
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("GC 结束");
}
}
}
这次我们可以看到run执行一次,listener也早早的被回收了。
更多系统回收资料参见:http://www.codeproject.com/Articles/29922/Weak-Events-in-C