0%

【C#】再谈委托

>无扰阅读模式(测试) 先报一下现况吧:目前正在做WPF版的Walkman Playlist Tools,增加和完善了很多功能,小小地图透一下: 具体改了什么功能,先卖个关子。 这么长时间没更真的没有在摸鱼哦!真的没有! 那么,我们步入正题吧:

委托

[delegate]


委托?

简单地说,委托的实质是函数的指针。

实现委托?

和其他类型一样,委托在实现之前也要先定义类,这个类称为委托,而委托函数的是委托的成员(可以看作委托类的对象)。

①Action委托

前面说过,委托在实现之前必先定义类,这个类可以自己定义(之后会讲),也可以直接使用系统定义好的委托类,这里讲的Action和下面的Func就是这样的类。 Action委托是可添加无传入值,且无返回值(void)方法的委托类。

创建:

1
Action anyname =new Action( FunctionHere );

调用(以下三条代码效果相同):

1
2
3
anyname.Invoke();//调用委托(完全版)
anyname();//调用委托(简写版)
FunctionHere();//原函数

②Func委托

Func委托是通过泛型容纳任何一种函数的委托类。

创建:

1
Func<Type1,Type2,...ReturnType> anyname=new Func<Type1,Type2,...ReturnType>( FunctionHere )

如上,泛型中可填入任意数量的数据类型,其中最后一个输入的数据类型用作规定函数的返回类型,其他(除了最后一个)数据类型可以规定函数的传入类型。需要注意的是,若只填入一个类型,那么这个类型是函数的返回类型。

调用:

1
2
3
anyname.Invoke();//调用委托(完全版)
anyname();//调用委托(简写版)
FunctionHere();//原函数

③委托的声明(自定义委托)

委托的声明方式与一般类不同,主要是为了照顾可读性和C、C++传统。 注意声明委托的位置,避免写错地方结果声明成嵌套类型。 委托所封装的方法必须类型兼容。

定义:

1
2
3
4
5
6
public delegate double Calc(doule x,double y);
class Program
{
Calc calc1=new Calc( FunctionHere );
......
}

调用:

1
2
3
calc1.Invoke((double)var,(double)var);//调用委托(完全版)
calc2((double)var,(double)var);//调用委托(简写版)
FunctionHere((double)var,(double)var);//原函数

④委托的使用

——当作数据传入函数

  • 用作模板(常位于代码中部,有返回值)
  • 回调方法(常位于代码尾部,无返回值,多用于执行后续操作)

注意:难精通、易使用、功能性强的功能一旦滥用后果非常严重 1. 这是一种方法级别的紧耦合,现实工作中使用应慎之又慎 1. 可使得程序可读性下降,doubug难度增加 1. 把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护 1. 使用不当可能造成内存泄漏和程序性能下降

⑤多播委托

刚刚演示的委托均为单播委托,即一个委托只有一个方法相应。由此推广,多播委托即为一个委托可有多个方法相应。

方法:

使用+=可在委托的成员中再添加一个方法。

演示:

1
2
3
4
5
6
7
8
9
10
Action action1=new Action(Function1);
Action action2=new Action(Function2);
Action action3=new Action(Function3);
action1+=action2;
action1+=action3;
action1.Invoke();
/*效果与分别调用相同:
action1.Invoke();
action2.Invoke();
action3.Invoke();*/

⑥异步调用

先明确几个称呼: 一个程序只有一个进程,但是可以有很多个线程

同步=串行=单线程; 异步=并行=多线程;

  • 直接同步调用:使用方法名
  • 间接同步调用:使用单播/多播委托的Invoke方法
  • 隐式异步调用:使用委托的BeginInvoke方法,如 csharp anyname.BeginInvoke(delegate,object) //第一个传入值为委托类型,标明该线程做完后做什么,什么都不做就填null //第二个传入值默认传入null 需要注意的是,这是相比主线程而分离的分支线程,也就是说使用n次BeginInvoke就调用了 n+1个线程
  • 显式异步调用: 1.Thread类:
1
2
3
4
5
6
7
8
9
Using System.Threading;
Thread thread1=new Thread(new ThreadStart(Function1));
Thread thread2=new Thread(new ThreadStart(Function2));
Thread thread3=new Thread(new ThreadStart(Function3));
//创建了三个线程,共四个线程。
thread1.Start();
thread2.Start();
thread3.Start();
//分别启动线程

2.Task类:

1
2
3
4
5
6
7
8
9
Using System.Threading.Task;
Task task1=new Task(new Action(Function1));
Task task2=new Task(new Action(Function2));
Task task3=new Task(new Action(Function3));
//注意这里给Task的构造方法传入值的其实是个委托
thread1.Start();
thread2.Start();
thread3.Start();
//分别启动线程

⑦用接口代替委托

Java的做法,降低耦合度,提高可读性