C# 事件
前言
本文只是为了复习,巩固,和方便查阅,一些知识点的详细知识会通过相关链接和文献标记出来。
为什么要用事件
要考虑这个问题之前,首先要知道使用委托的缺点,先看下面几段代码。其中包括了订阅DelegateSubscribe
和发布DelegatePublish
namespace Event
{
public class DelegatePublish
{
public int Value
{
get => _value;
set
{
_value = value;
Changed?.Invoke(_value);
}
}
private int _value { get; set; }
public Action<int> Changed;
}
public class DelegateSubscribe
{
public void ValueChanged(int value)
{
Console.WriteLine(value);
}
}
}
在主函数里面使用,一切顺利,它应该会被触发 Console.WriteLine(value);
// See https://aka.ms/new-console-template for more information
using Event;
var publish = new DelegatePublish();
var subscribe = new DelegateSubscribe();
publish.Changed += subscribe.ValueChanged;
publish.Value = 1;
一看没啥问题,订阅触发都正常,但这里有个可以操作的地方,就是这个changed
可以被DelegatePublish
以外的地方修改。如以下代码表示,实际上并没有值更新,但依旧可以触发。
// See https://aka.ms/new-console-template for more information
using Event;
var publish = new DelegatePublish();
var subscribe = new DelegateSubscribe();
publish.Changed = subscribe.ValueChanged;
publish.Changed.Invoke(1);
为了解决以上提到的问题,我们使用了event进行解决,只有直接持有这个事件的对象的类才可以调用事件,其他类只能使用+-和-=对这个事件进行添加或删除
public class DelegatePublish
{
public int Value
{
get => _value;
set
{
_value = value;
OnChanged?.Invoke(this,new ValueArgs(_value));
}
}
private int _value { get; set; }
public event EventHandler<ValueArgs> OnChanged;
public class ValueArgs:EventArgs
{
public ValueArgs(int newValue)
{
NewValue = newValue;
}
public int NewValue { get; set; }
}
}
void Publish_OnChanged(object? sender, DelegatePublish.ValueArgs e)
当然根据代码规范,其声明为object? sender
表示委托对象的引用, EventArgs
附带了数据
如果不喜欢参数e,还可以这么写,这样声明的就变成了 void Publish_OnChanged(object sender, DelegatePublish.ValueArgs newValue)
public event ChangedHandler OnChanged;
public delegate void ChangedHandler(object sender, ValueArgs newValue);
事件的内部机制
把DelegatePublish
代码翻译成IL,可以发现 add_OnChanged
和 remove_OnChanged
分别表示 +=和-=。最后由add
和remove
进行封装处理
.class public auto ansi beforefieldinit
Event.DelegatePublish
extends [System.Runtime]System.Object
{
.field private class [System.Runtime]System.EventHandler`1<class Event.DelegatePublish/ValueArgs> OnChanged
.method public hidebysig specialname instance void
add_OnChanged(
class [System.Runtime]System.EventHandler`1<class Event.DelegatePublish/ValueArgs> 'value'
) cil managed
} // end of method DelegatePublish::add_OnChanged
.method public hidebysig specialname instance void
remove_OnChanged(
class [System.Runtime]System.EventHandler`1<class Event.DelegatePublish/ValueArgs> 'value'
) cil managed
} // end of method DelegatePublish::remove_OnChanged
.event class [System.Runtime]System.EventHandler`1<class Event.DelegatePublish/ValueArgs> OnChanged
{
.addon instance void Event.DelegatePublish::add_OnChanged(class [System.Runtime]System.EventHandler`1<class Event.DelegatePublish/ValueArgs>)
.removeon instance void Event.DelegatePublish::remove_OnChanged(class [System.Runtime]System.EventHandler`1<class Event.DelegatePublish/ValueArgs>)
} // end of event DelegatePublish::OnChanged
} // end of class Event.DelegatePublish
这样我们知道了事件内部是怎么处理的,就可以自定义+=和-=了
public class DelegatePublish
{
public int Value
{
get => _value;
set
{
_value = value;
_OnChanged?.Invoke(this, new ValueArgs(_value));
}
}
public event EventHandler<ValueArgs> OnChanged
{
add => _OnChanged = (ChangedHandler)Delegate.Combine(value, _OnChanged);
remove => _OnChanged = (ChangedHandler)Delegate.Remove(_OnChanged, value)!;
}
public delegate void ChangedHandler(object sender, ValueArgs newValue);
private int _value { get; set; }
protected event ChangedHandler? _OnChanged;
public class ValueArgs : EventArgs
{
public ValueArgs(int newValue)
{
NewValue = newValue;
}
public int NewValue { get; set; }
}
}