[C#]詳細19委託研究


このセクションの内容
ここに画像を挿入説明

何を委託していますか?

JavaのからC ++で開発されたが、あなたのアプリケーションのセキュリティを向上させるために、元、プログラマーにメモリアドレスへの直接アクセスを禁止しますが、このタイプのデータの「信頼」を通じてC#が保持され、関数ポインタは、機能に対応しました
ここに画像を挿入説明

1.関数ポインタとは何ですか?

関数ポインタは、関数へのポインタであります変数だから、それ自体が「関数ポインタ」は、第1のポインタ変数が、関数へのポインタ変数ポイントでなければなりません。関数呼び出しと関数のパラメータを作成する:関数ポインタ変数を使用すると、ちょうど2つの目的のために、変数、関数ポインタの他のタイプのポインタ変数の参照と同様に、使用可能な関数ポインタ変数を呼び出します。

2.どのように「すべてのアドレス」を理解するには?

プログラム=アルゴリズム+データ

  • データ変数に格納され、変数の性質:メモリのセクションから開始変数名に対応するメモリアドレス、このメモリは、データ変数に格納され、どのようにこのメモリ、変数のデータ・タイプに応じ-変数(データ)のアドレスであります
  • 関数は、アルゴリズムであり、関数アドレスエッセンス:メモリのための出発点として、関数名に対応するメモリアドレスであり、このメモリは、値が格納されているが、機械言語の命令セットされていない、CPUは、命令のセットに従っていますアドレスの機能-それによってアルゴリズムのすべての機能に含まを完了し、一つずつ実行

自然の呼び出しの3直接的および間接呼び出し

  • 直接呼び出し:、関数名で関数を呼び出す関数のアドレスを関数名で直接得られるCPUおよび実装を開始します
  • 間接呼び出しは:アドレスが関数の値を読み取ることにより得られ、実行が開始されるCPU、関数ポインタを介して関数ポインタ変数として、関数名が対応するアドレスに格納され、関数を呼び出すこと

无论是直接调用还是间接调用,在程序执行完函数内的最后一条指令,CPU都会返回到调用者那里继续执行;两种调用在效果上是一样的

4.Action委托与Func委托

Action 与 Func是.NET类库中增加的内置委托,用以更简洁方便地使用委托
最初使用委托时,均需要先定义委托类型,然后定义一个符合委托类型签名的函数,在调用前,需声明并创建委托对象,将指定函数与委托进行关联,例如:
ここに画像を挿入説明
如果委托中的参数需要增加至三个,四个以致更多,则需要再次定义委托类型;因此微软推出了对此进行简化的内置委托类型:也就是Action和Func,简化了这些不必要的操作。
内置委托类型,顾名思义Action和Func本身就是已经定义好的委托类型。两种委托类型的区别在于:Action委托签名不提供返回类型,而Func提供返回类型。

Action委托具有Action、Action<T1,T2>、Action<T1,T2,T3>……Action<T1,……T16>多达16个的重载,其中传入参数均采用泛型中的类型参数T,涵盖了几乎所有可能存在的无返回值的委托类型。

Func则具有Func、Func<T,Tresult>、Func<T1,T2,T3……,Tresult>17种类型重载,T1……T16为出入参数,Tresult为返回类型。

代码示例:
ここに画像を挿入説明

5.为什么说委托是函数指针的升级版?

因为委托可以按照一定的约束指向某些目标方法,从而帮助我们完成对这些方法的间接调用

自定义委托的声明与使用

1.代码示例

ここに画像を挿入説明ここに画像を挿入説明

//自定义委托的声明与使用
public delegate double Calc(double a,double b );//委托应声明在名称空间体中
class Program
{
	static void Main(string[] args)
	{
		//创建类的实例,以便可以让委托封装实例方法
		Calculator calculator = new Calculator();
		Calc calc1 = new Calc(calculator.Add);
		Calc calc2 = new Calc(calculator.Sub);
		Calc calc3 = new Calc(calculator.Mul);
		Calc calc4 = new Calc(calculator.Div);
		double a = 100;
		double b = 200;
		double c = 0;
		clac1.Invoke(a,b);//也可以写成:clac1(a,b);
		calc2(a,b);
		clac3(a,b);
		clac4(a,b);
	}
}
//声明一个类,在类中包含于自定义的委托类型相兼容的方法
class Calculator
{
	public double Add(double x,double y)
	{
		return x + y;
	}
	public double Sub(double x,double y)
	{
		return x - y;
	}
	public double Mul(double x,double y)
	{
		return x * y;
	}
	public double Div(double x,double y)
	{
		return x / y;
	}
}

2.自定义委托需要注意

(1)委托与所封装的方法必须 “类型兼容” ,委托是否能封装且指向这个方法,是由参数类型和返回值类型决定的,与参数名无关

(2)声明委托时不要放错位置,应放在名称空间体中,委托与其他类是同一个级别
【但由于C#是允许有嵌套类型出现的,也就是在一个类中可以嵌套声明另一个类,如果把委托声明在一个类体中,那么此时的委托就已经不是一个独立的类了,而是一个嵌套类;在使用嵌套类时,一般都会这样写:Program.Calc,表示Calc委托是Program类的嵌套类】

委托的一般使用

ここに画像を挿入説明
委托的一般使用是把委托当作方法的参数传进方法中,正确使用有模板方法和回调(callback)方法
这样做的好处是:可以形成动态调用方法的代码结构

1.什么是模板方法?

模板方法:该方法通过传进来的委托参数借用指定的外部方法产生一个结果;
就相当于在模板方法中有一个填空题,空白之处就用待传进来的委托类型的参数进行填补,这个委托类型的参数间接调用指定的外部方法;该方法一般是具有返回值的,一旦拿到这个返回值之后,继续执行后面的逻辑

  • 相当于 “填空题”
  • 常位于代码中部
  • 委托有返回值

2.模板方法代码示例

实现:生产工厂生产多种产品,交由包装工厂打包

(1)首先是准备工作,声明四个类,分别是:
产品类:具有产品名称属性
箱子类:具有产品属性(箱子中封装着什么产品)
包装工厂类:具有模板方法,该方法用以将产品打包,返回一个箱子
产品工厂类:具有生产产品的两个方法,返回一个产品
ここに画像を挿入説明

(2)创建类的实例,调用模板方法
ここに画像を挿入説明

3.使用模板方法的好处?

使用模板方法可以最大限度地使代码复用(Reuse),代码的复用不但可以提高工作效率,还可以减少bug的引用,良好的复用结构是所有的优秀软件所追求的共同目标之一;

比如说上述例子,使用了模板方法wrapproduct()后,Product类,Box类,WrapFactory类都不用再动,只需不停地去扩展ProductFactory类,往里增添生产各种产品的方法,将这些方法封装在委托实例里传给模板方法,模板方法就一定能够把这些产品封装成箱交还出来

4.什么是回调方法?

回调关系:可以调用某个方法,也可以不调用;用得着就调用,用不着就不调用(回调方法也叫好莱坞方法)
回调方法给了我们一个机会:可以动态选择后续将被调用的方法

当以回调方法的形式来调用委托时,需要把委托类型的参数传进主调方法中,被传进主调方法里的委托类型的参数内部,封装着一个被回调的方法(回调方法),主调方法会根据自己的逻辑来决定是否调用该回调方法
一般情况下,主调方法会在主要逻辑执行完毕后,决定是否调用回调方法,因此回调方法一般都位于主调方法代码的末尾处;而且回调方法一般的用处是来执行一些后续工作,并没有返回值

  • 相当于 “流水线”
  • 常位于代码末尾
  • 委托无返回值

5.回调方法代码示例

实现:如果所生产的产品的价格大于100,就把生产记录打印出来

(1)首先为产品类中创建 “价格” 属性,再声明一个Logger类(记录程序运行状态),该类中有一个log()方法,用以打印出生产记录,没有返回值
ここに画像を挿入説明

(2)将log()方法以回调方法的形式传进wrapproduct()方法,并添加逻辑决定是否调用这个回调方法
ここに画像を挿入説明

(3)修改产品信息,Pizza价格定为50,ToyCar价格定为130
ここに画像を挿入説明

(4)将log()方法封装进一个委托实例,并将该委托实例作为参数传进wrapproduct()方法
ここに画像を挿入説明

无论是模板方法还是回调方法,其本质都是一样的,就是用委托类型的参数封装一个外部方法,通过将委托参数传进我们自己的方法,进而实现对外部方法的间接调用

6.滥用委托的严重后果

(1)委托是一种方法级别的紧耦合,一旦滥用,后果非常严重;所以现实工作中要慎之又慎

(2)滥用耦合会使代码可读性下降,debug的难度增加

(3)委托使用不当有可能造成内存内存泄漏和程序性能下降

  • 内存泄漏:委托会引用着一个方法,如果该方法是实例方法,那么这个方法必定会属于一个对象;拿委托引用着这个方法,这个对象就必然会存在于内存当中,即便是没有其他的引用变量引用着该对象,该对象所占内存也不会被释放,因为一旦释放,委托就无法再去间接调用这个对象的实例方法了

(4)把委托回调,异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
代码示例:(水平高了再回来品这段代码)48min
ここに画像を挿入説明

委托的高级使用

ここに画像を挿入説明

1.什么是多播委托?

单播委托:一个委托封装着一个方法
多播委托:一个委托封装着多个方法

  • 单播委托代码示例
    ここに画像を挿入説明
    ここに画像を挿入説明

  • 多播委托代码示例
    ここに画像を挿入説明
    在调用多播委托时,其执行顺序是按照封装方法的先后顺序来执行的

2.同步调用与异步调用

  • 每一个程序运行起来都是一个进程(Process),每一个程序都包含一个或多个线程;
    当程序启动时,会形成一个进程,进程中第一个运行起来的线程就是进程(程序)的主线程,其他都是分支线程;

  • 同步:你做完了,我(在你的基础之上)接着做
    异步:咱们两个同时做

  • 当在同一个线程内调用方法时,方法的执行是前一个执行完了,后一个才会得到执行;这种在同一个线程内依次执行的方法调用叫作同步调用
    异步调用,又称多线程调用,在不同线程中调用方法,每个线程互不相干(指逻辑互不相干),一个线程的开始或结束并不会影响另一个线程的开始或结束,而且多个线程的开始与结束的时机又可能构成不同的组合
    【现实工作中经常会遇到多个线程共享(即同时访问)同一个资源(比如某个变量)的情况,这时如果处理不当就会产生线程间争夺资源的冲突,如何避免多线程程序中的资源冲突是一个高级话题】

  • 同步调用:在单线程中进行串行调用
    异步调用:在多线程中进行并行调用

3.同步调用的三种形式

(1)直接同步调用
(2)单播委托的间接同步调用
(3)多播委托的间接同步调用

4.异步调用

(1)隐式异步调用
使用委托的BeginInvoke()方法
该方法会自动生成分支线程,然后在分支线程中调用所封装的方法
ここに画像を挿入説明
ここに画像を挿入説明
待解问题:出现了本不应该出现的同色情况是因为发生了资源争抢(这三个线程都在访问控制台的前景色)

(2)显式异步调用
<1>使用Thread
ここに画像を挿入説明
<2>使用Task
ここに画像を挿入説明

5.设计模式常识

应当适时地使用**接口(interface)**取代一些对委托的使用;因为如果对委托使用不当,会极大地降低代码可读性,且造成debug困难,使用接口可以避免这些不必要的麻烦。而且可以获得相应功能

  • 如何使用接口取代委托 74min
公開された29元の記事 ウォンの賞賛3 ビュー941

おすすめ

転載: blog.csdn.net/weixin_44813932/article/details/103979607
おすすめ