什么是事件
事件是基于委托之上的新特性,自然也是 C# 独有的概念。事件理解起来不难,意思就是他的字面意思,就是我们日常理解的事件
使用事件
事件与委托代码上的区别就是多了一个 event 关键字,使用方式有点变化
如何定义事件
声明上就是多了一个 event,如下所示
代码语言:javascript复制// EventHandler 是在 System 命名空间下定义的委托类型
public event EventHandler birthday;
// 自定义
public delegate void CustomHandler;
public event CustomHandler custom;
订阅和取消事件
我这里基于事件来实现博客的发布、订阅和取消订阅操作。具体逻辑就是:读者张三、李四订阅了某博主,博主发布了一篇博客,张三、李四能正常接受博客发布通知。张三不想再接受这个博主的博客推送了,取消订阅,之后再也收不到这位博主的博客通知了
如果不适用事件特性,我们怎么实现?
代码语言:javascript复制public class AuthorBlog
{
public List<Reader> Readers = new List<Reader>();
public void Publish(string name)
{
Console.WriteLine("作者发布了:" name);
foreach (var reader in Readers)
{
reader.Receive(name);
}
}
}
public class Reader
{
public string name { get; set; }
public void Subscribe(AuthorBlog blog)
{
blog.Readers.Add(this);
}
public void Unsubscribe(AuthorBlog blog)
{
blog.Readers.Remove(this);
}
public void Receive(string name)
{
Console.WriteLine($"{this.name}接受到了博客:{name}");
}
}
class Program
{
static void Main(string[] args)
{
var blog = new AuthorBlog();
var user1 = new Reader();
user1.name = "张三";
user1.Subscribe(blog);
var user2 = new Reader();
user2.name = "王二";
user2.Subscribe(blog);
blog.Publish("《钢铁是怎样炼成的》");
user1.Unsubscribe(blog);
blog.Publish("《边城》");
Console.ReadKey();
}
}
很简单,我用一个集合将读者对象存储起来,我每次发布都去循环去调用读者的接受方法。或者做的再好一点就是只存储读者对象的 Receive 方法
代码语言:javascript复制public delegate void ReceiveHandler(string name);
public ReceiveHandler Receive;
// Receive = ...
// Receive -= ...
那 event 有什么作用呢?我们看下面这个代码示例
代码语言:javascript复制class Program
{
public delegate void PublishHandler(string name);
public class AuthorBlog
{
public event PublishHandler Publish;
public void OnPublishing(string name)
{
Console.WriteLine("作者发布了:" name);
if (Publish != null)
{
Publish(name);
}
}
}
public class Reader
{
public string? name { get; set; }
public void Subscribe(AuthorBlog blog)
{
blog.Publish = Receive;
}
public void Unsubscribe(AuthorBlog blog)
{
blog.Publish -= Receive;
}
public void Receive(string name)
{
Console.WriteLine($"{this.name}接受到了博客:{name}");
}
}
static void Main(string[] args)
{
var blog = new AuthorBlog();
var user1 = new Reader();
user1.name = "张三";
user1.Subscribe(blog);
var user2 = new Reader();
user2.name = "王二";
user2.Subscribe(blog);
blog.OnPublishing("《钢铁是怎样炼成的》");
user1.Unsubscribe(blog);
blog.OnPublishing("《边城》");
Console.ReadKey();
}
}
我们多加了一个OnPublishing
方法,在这个方法里我们去执行了事件Publish
,那么不加这个event
有什么影响?
区别在于 调用方式:
加event
无法通过blog.Publish(...)
执行委托链,只能在类内部调用
不加event
可以通过blog.Publish(...)
执行委托链
EventHandler
前面我们用自定义委托类型来定义事件,我们还可以用 .Net 类库中预定义的委托类型EventHandler
来定义事件,这也是实际开发中普遍采用的一种方式
定义
定义如下
代码语言:javascript复制public delegate void EventHandler(Object sender, EventArgs e);
我们可以看出以下几点
- 该委托返回类型为 void,因此实例化委托类型的方法也需要满足这点
- 第一个参数 sender 负责保存对触发事件对象的引用,其类型为 object
- 第二个参数 e 负责保存事件数据,
EventArgs
类也是 .Net 类库中定义的类,它不保存任何数据
扩展 EventArgs 类
因为EventHandler
只用于处理不包含事件数据的事件。如果我们想要在由这种方式定义的事件中包含事件数据,则可以通过派生EventArgs
类实现。实现代码如下
class Program
{
public delegate void PublishHander(object sender, BlogAuthorEventArgs e);
public class AuthorBlog
{
public event PublishHander PublishEvent;
public void OnPublishing(string name)
{
Console.WriteLine("作者发布了:" name);
if (PublishEvent != null)
{
PublishEvent(this, new BlogAuthorEventArgs());
}
}
}
public class Reader
{
public string? name { get; set; }
public void Subscribe(AuthorBlog blog)
{
blog.PublishEvent = new PublishHander(Receive);
}
public void Unsubscribe(AuthorBlog blog)
{
blog.PublishEvent -= new PublishHander(Receive);
}
public void Receive(object s, BlogAuthorEventArgs args)
{
Console.WriteLine($"{this.name}接受到了博客:{name}");
}
}
public class BlogAuthorEventArgs : EventArgs
{
public string name { get; set; }
public BlogAuthorEventArgs() { }
public BlogAuthorEventArgs(string name)
{
this.name = name;
}
}
static void Main(string[] args)
{
var blog = new AuthorBlog();
var user1 = new Reader();
user1.name = "张三";
user1.Subscribe(blog);
var user2 = new Reader();
user2.name = "王二";
user2.Subscribe(blog);
blog.OnPublishing("《钢铁是怎样炼成的》");
user1.Unsubscribe(blog);
blog.OnPublishing("《边城》");
Console.ReadKey();
}
}
事件的本质
从事件的使用过程可以看出,事件的定义包含了委托类型。那么,事件和委托间到底有着怎样的关系呢?
代码语言:javascript复制class Program
{
public delegate void PublishHandler(string name);
public event PublishHandler PublishEvent;
static void Main(string[] args)
{
}
}
可以通过 IL 工具去查看中间语言
归纳总结
看到这里,基本上你已经掌握了 C# 1.0 最重要的两个特性了。看到这里大家肯定觉得 C# 函数传递非常麻烦,虽然能实现,但是要写很多代码,还能不能再方便一点?