C#委托(delegate、Action、Func、predicate)和事件
⼀、前⾔
刚开始⼯作的时候,觉得委托和事件有些神秘,⽽当你理解他们之后,也觉得好像没有想象中的那么难。在项⽬中运⽤委托和事件,你会发现他⾮常棒,这篇博⽂算是⾃⼰对委托和事件的⼀次梳理和总结。
⼆、委托
C#中的委托,相当于C++中的指针函数,但委托是⾯向对象的,是安全的,是⼀个特殊的类,当然他也是引⽤类型,委托传递的是对⽅法的引⽤。
2.1、delegate
酸白菜怎么做好吃声明委托就必须使⽤关键字“delegate”,委托是先声明,后实例化。⾄少0个参数,⾄多32个参数
格式如下所⽰:
private delegate string GetAsString();
委托是⼀个类,所以他的实例化跟类的实例化⼀样,只是他总是接受⼀个将委托⽅法作为参数的构造函数。调⽤委托⽅法就有两种⽅式,如下所⽰:
咕噜肉的做法int i = 10;
var method = new GetAsString(i.ToString);
//调⽤⽅法⼀
Console.WriteLine($"method⽅法{method()}");
//调⽤⽅法⼆
Console.WriteLine($"method.Invoke⽅法{method.Invoke()}");
运⾏结果:
2.2、Action
Action是⽆返回值的泛型委托,可以接受0个⾄16个传⼊参数
Action 表⽰⽆参,⽆返回值的委托
Action<int,string> 表⽰有传⼊参数int,string⽆返回值的委托
降妖捉怪前⾯我们【】中,就使⽤了Action。如:
public static void Debug(string message, Action RegistedProperties)
{
RegistedProperties();
log.Debug(message);
}
调⽤⽅式为:
PFTLog.Debug("测试扩展字段", () => {
LogicalThreadContext.Properties["LogType"] = "扩展字段内容";
});
在运⾏中,直接运⾏Action中的内容即可。
2.3、Func
Func是有返回值的泛型委托,可以接受0个⾄16个传⼊参数
Func<int> 表⽰⽆参,返回值为int的委托
Func<object,string,int> 表⽰传⼊参数为object, string 返回值为int的委托
public static decimal GetTotal(Func<int, int, decimal> func, int a, int b)
{
return func(a, b);
}
调⽤⽅式
var total = GetTotal((a, b) => { return (decimal)a + b; }, 1, 2);
Console.WriteLine($"结果为{total}");
运⾏结果
2.4、predicate
predicate 是返回bool型的泛型委托,只能接受⼀个传⼊参数
predicate<int> 表⽰传⼊参数为int 返回bool的委托
定义⼀个⽅法:
public static bool FindPoints(int a)
{
return a >= 60;
}
定义Predicate委托
Predicate<int> predicate = FindPoints;
调⽤
var points = new int[] {
10,
50,
60,
80,
100 };
var result = Array.FindAll(points, predicate);
Console.WriteLine($"结果为{string.Join(";", result)}");
西安到武汉多少公里运⾏结果
2.5、多播委托
前⾯的只包含了⼀个⽅法的调⽤,委托可以包含多个⽅法,这种委托就叫做多播委托。多播委托利⽤“+=”和“-+”两种运算符进⾏添加和删除委托。
先定义两个⽅法
public static void MultiplyByTwo(double v)
{
double result = v * 2;
Console.WriteLine($"传值:{v};MultiplyByTwo结果为{result}");
}
public static void Square(double v)
{
double result = v * v;
Console.WriteLine($"传值:{v};Square结果为{result}");
}
然后调⽤
Action<double> operations = MultiplyByTwo;
operations(1);
operations += Square;
operations(2);
临夏美食运⾏结果:
三、事件
事件是基于委托,为委托提供⼀种发布/订阅机制,声明事件需要使⽤event关键字。
发布者(Publisher):⼀个事件的发⾏者,也称作是发送者(nder),其实就是个对象,这个对象会⾃⾏维护本⾝的状态信息,当本⾝状态信息变动时,便触发⼀个事件,并通知说有的事件订阅者;
订阅者(Subscriber):对事件感兴趣的对象,也称为Receiver,可以注册感兴趣的事件,在事件发⾏者触发⼀个事件后,会⾃动执⾏这段代码
是不是看到nder,就有种很熟悉的感觉先不忙着急,我们先看下事件的声明和使⽤
有这样⼀个应⽤场景,如果系统有异常,需要及时的通知管理员。那么需要在我们的⽇志记录⾥⾯添加通知管理员的功能,但是问题来了,该怎么通知管理员呢?⾄少现在⽆法知道。所以我们就需要在使⽤到事件。
添加代码如下,如果不知道⽇志功能的可以参考【】
//声明⼀个通知的委托
public delegate void NoticeEventHander(string message);
//在委托的机制下我们建⽴以个通知事件
public static event NoticeEventHander OnNotice;
调⽤⽅式
public static void Debug(string message, Action RegistedProperties)
{
RegistedProperties();
log.Debug(message);
//执⾏通知
OnNotice?.Invoke($"系统异常,请及时处理,异常信息:{message}");
}
在引⽤场景的代码,先定义⼀个通知管理员的⽅法(这⾥我们直接Console.WriteLine出来)
public static void Notice(string message)
{
Console.WriteLine($"通知内容为{message}");
}
先注册,然后触发异常消息
//注册⽅式⼀
PFTLog.OnNotice += Notice;
//注册⽅式⼆
//PFTLog.OnNotice += new PFTLog.NoticeEventHander(Notice);
PFTLog.Debug("测试扩展字段", () => {
LogicalThreadContext.Properties["LogType"] = "扩展字段内容";
});
运⾏结果
这⾥⾯我只需要定义好发布者,你可以以任何⽅式订阅,是不是很⾮常简单。
弄明⽩了上⾯的事件,我们在来说说.Net经常出现的object nder和EventArgs e
.Net Framework的编码规范:
⼀、委托类型的名称都应该以EventHandler结束
⼆、委托的原型定义:有⼀个void返回值,并接受两个输⼊参数:⼀个Object 类型,⼀个 EventArgs类型(或继承⾃EventArgs)
三、事件的命名为 委托去掉 EventHandler之后剩余的部分
四、继承⾃EventArgs的类型应该以EventArgs结尾
现在我们以⼀个新书发布的⾃定义事件为例
创建对应的类⽂件:
事件者发布代码:
public class BookInfoEventArgs : EventArgs
{
public BookInfoEventArgs(string bookName)
{
BookName = bookName;
}
public string BookName { get; t; }
}
public class BookDealer
{
//泛型委托,定义了两个参数,⼀个是object nder,第⼆个是泛型 TEventArgs 的e
//简化了如下的定义
//public delegate void NewBookInfoEventHandler(object nder, BookInfoEventArgs e);
/
/public event NewBookInfoEventHandler NewBookInfo;
public event EventHandler<BookInfoEventArgs> NewBookInfo;2022清明
public void NewBook(string bookName)
{
RaiNewBookInfo(bookName);
}
public void RaiNewBookInfo(string bookName)
{
NewBookInfo?.Invoke(this, new BookInfoEventArgs(bookName));
}
}
事件订阅者
public class Consumer
{
public Consumer(string name)
{
毕节特产Name = name;
}
public string Name { get; t; }
public void NewBookHere(object nder, BookInfoEventArgs e)
{
Console.WriteLine($"⽤户:{Name},收到书名为:{ e.BookName}");
}
}
事件订阅和取消订阅
var dealer = new BookDealer();
var consumer1 = new Consumer("⽤户A");
dealer.NewBookInfo += consumer1.NewBookHere;
dealer.NewBook("book112");
var consumer2 = new Consumer("⽤户B");
dealer.NewBookInfo += consumer2.NewBookHere;
dealer.NewBook("book_abc");
dealer.NewBookInfo -= consumer1.NewBookHere;
dealer.NewBook("book_all");
运⾏结果
经过这个例⼦,我们可以知道Object nder参数代表的是事件发布者本⾝,⽽EventArgs e 也就是监视对象了。深⼊理解之后,是不是觉得也没有想象中的那么难了。
66岁四、总结
这⾥我们讲了委托和事件,在.Net开发中使⽤委托和事件,可以减少依赖性和层的耦合,开发出具有更⾼的重⽤性的组件。