【方法】
- 在注册前先检查该订阅者是否已经注册过
- 在注册前先移除然后再注册
【代码实现】
using System;
using System.Collections.Generic;
using System.Reflection;
namespace 笔试
{
class Program
{
static void Main(string[] args)
{
MyDelegate myDelegate=new MyDelegate();
Test test=new Test();
Console.WriteLine("每次注册前先检查是否重复:");
if (TryAdd(myDelegate.valueChange,"Fun1"))
myDelegate.valueChange += test.Fun1;
if (TryAdd(myDelegate.valueChange, "Fun1"))
myDelegate.valueChange += test.Fun1;
if (TryAdd(myDelegate.valueChange, "Fun2"))
myDelegate.valueChange += test.Fun2;
myDelegate.CurNum = 10;
myDelegate.Mul(5);
Console.WriteLine("每次注册前先移除:");
myDelegate.numAction += test.Fun1;
myDelegate.numAction += test.Fun1;
myDelegate.numAction += test.Fun2;
myDelegate.Add(6);
Console.ReadKey();
}
public static bool TryAdd(MyDelegate.numDelegate tempDelegate,string methodName)
{
if (tempDelegate == null)//委托为空直接返回
return true;
Delegate[] delegates = tempDelegate.GetInvocationList();//获取委托列表
for (int i = 0; i < delegates.Length; i++)
{
if (delegates[i].GetMethodInfo().Name.Equals(methodName))//遍历委托列表,通过反射获取注册的方法名,判断与待注册的方法名是否相等
{
return false;
}
}
return true;
}
}
public class MyDelegate
{
public delegate void numDelegate(int x);
public numDelegate valueChange;
private int _CurNum;
public int CurNum
{
get { return _CurNum; }
set
{
_CurNum = value;
numDelegate tempDelegate = valueChange;//将委托引用复制到局部变量中,确保在检查空值和通知之间,所有订阅者被另一个线程移除时不会引发空引用异常
if(tempDelegate!= null)//调用委托前检查是否为空
tempDelegate(value);//通知所有订阅者
}
}
public void Mul(int x)
{
_CurNum *= x;
numDelegate tempDelegate = valueChange;
tempDelegate?.Invoke(_CurNum);//判空+通知的简写方式
}
//自定义实现add和remove块,类似属性
private Action<int> _numAction;
public event Action<int> numAction//自定义事件的add和remove块,类似自定义get和set
{
add
{
_numAction -= value;//每次添加前,先删除订阅者,然后添加订阅者就防止重复注册事件
_numAction += value;
}
remove
{
_numAction -= value;
}
}
public void Add(int x)
{
_CurNum += x;
_numAction(_CurNum);//事件不用检查是否为空
}
}
public class Test
{
public void Fun1(int val)
{
Console.WriteLine("值增加2倍" + val*2);
}
public void Fun2(int val)
{
Console.WriteLine("值减小2倍" + val/2);
}
}
}
【代码输出】