C#之委托

目录

一、简介

(一)概述

(二)类与委托

二、声明委托

三、使用委托

(一)创建委托对象

(二)使用委托

四、简单的委托示例

五、Action[T]和Func[T]委托

六、多播委托

七、匿名方法


        委托时寻址方法的.NET版本。在C++中,函数指针只不过是一个指向内存位置的指针,它不是类型安全的。我们无法判断这个指针实际指向什么,像参数和返回类型等项就无从知晓了。

        而.NET委托完全不同,委托是类型安全的类,它定义了返回类型和参数的类型。委托类不仅包含对方法的引用,也可以包含对多个方法的引用。

        Lambda表达式与委托之间相关。当参数是委托类型时,就可以使用Lambda表达式实现委托引用的方法。

一、简介

(一)概述

C#中的委托是一个类型,它描述了一个方法的签名,即方法的参数类型和返回类型。委托可以看作是一个指向方法的引用,使得我们可以像使用函数指针一样调用这些方法。

  • 将一个或多个方法作为参数传递给另一个方法,从而在需要时调用这些方法。
  • 实现事件处理程序。
  • 实现回调方法。
  • 实现异步编程等功能。

        我们习惯于把数据作为参数传递给方法,所以,给方法传递另一个方法听起来有点奇怪。而有时某个方法执行的操作并不是针对数据进行的,而是要对另一个方法进行操作。更麻烦的是,在编译时我们不知道第二个方法是什么,这个信息只能在运行时得到,所以需要把第二个方法作为参数传递给第一个方法。

        在C和C++中,只能提取函数的地址,并作为一个参数传递它。C没有类型安全性,可以把任何函数传递给需要函数指针的方法。但是,这种直接方法不仅会导致一些关于类型安全性的问题,而且没有意识到:在进行面向对象编程时,几乎没有方法是孤立存在的,而是在调用方法前通常需要与类实例相关联。所以.NET Framework在语法上不允许使用这种直接方法。如果要传递方法,就必须把方法的细节封装在一种新类型的对象中,即委托。委托只是一种特殊类型的对象,其特殊之处在于,我们以前定义的所有对象都包含数据,而委托包含的只是一个或多个方法的地址。

(二)类与委托

委托和类一样,是一种用户定义类型。但类表示的是数据和方法的集合,而委托则持有一个或多个方法,以及一系列预定义操作。可以通过一下操作步骤来使用委托。

(1)声明一个委托类型。委托声明看上去和方法声明相似,只是没有实现块。

(2)使用该委托类型声明一个委托变量

(3)创建一个委托类型的对象,并把它赋值给委托变量。新的委托对象包含指向某个方法的引用,这个方法的签名和返回类型必须跟第一步中定义的委托类型一致。

(4)你可以选择为委托对象添加其他方法。这些方法的签名和返回类型必须与第一步中定义的委托类型相同

(5)在代码中你可以像调用方法一样调用委托。在调用委托的时候,其包含的每一个方法都会被执行。

委托
声明类型声明类声明委托(类型)
声明类型的变量声明类类型的变量声明委托类型的变量
填充变量创建类的实例并且把他的引用赋值给变量创建委托的实例并且把它的引用赋值给变量,然后增加第一个方法
使用变量使用类对象调用委托对象

二、声明委托

在C#中使用一个类时,分两个阶段:

  • 需要定义这个类,即告诉编译器这个类由什么字段和方法组成。
  • 实例化类的一个对象

使用委托时,也需要经过这两个步骤:

  • 定义要使用的委托。对于委托,定义它就是告诉编译器这种类型的委托表示哪种类型的方法。
  • 必须创建该委托的一个或多个实例。编译器在后台将创建表示该委托的一个类。

定义委托的语法如下:

delegate void IntMethodInvoker(int x);

关键字:delegate        返回类型:void        委托类型名称:IntMethodInvoker        签名:int x

        

在这个示例中,定义了一个委托IntMethodInvoker,并指定该委托的每个实例都可以包含一个方法的引用,该方法带有一个int参数,并返回void。理解委托的一个要点是它们的类型安全性非常高。在定义委托时,必须给出它所表示的方法的签名和返回类型等全部细节。

假定要定义一个委托 TwoLongsop,该委托表示的方法有两个 long型参数,返回类型为 double。可以编写如下代码:

delegate double TwoLongsOp(long first, long second);
或者要定义一个委托,它表示的方法不带参数,返回一个 s缸呜 型的值,可以编写如下代码:
delegate string GetAString();

其语法类似于方法的定义,但没有方法体,定义的前面要加上关键字delegate。因为定义委托基本是定义一个新类,所以可以在定义类的任何相同地方定义委托。也就是说,可以在另一个类的内部定义,也可以在任何类的外部定义,还可以在名称空间中把委托定义为顶层对象。更具定义的可见性,和委托的作用域,可以在委托的定义上应用任意常见的访问修饰符:public、private、protected等:

public delegate string GetAstring();

定义好委托后,就可以创建它的一个实例,从而用它存储特定方法的细节。

三、使用委托

(一)创建委托对象

委托是引用类型,因此有引用和对象。在委托类型声明之后,我们可以声明变量并创建类型的对象。

有两种声明委托的方式:

(1)使用带new运算符的对象创建表达式。new运算符的操作数的组成如下:

  • 委托类型名
  • 一组圆括号,其中包含作为调用列表中第一个成员的方法的名换成呢个。该方法可以是实例方法也可以是静态方法
//创建委托并保存引用
GetAString delVar = new GetAString(myInstObj.MyM1);//实例方法
dVar = new MyDel(SClass.OtherM2); //静态方法

(2)还可以使用快捷语法,它仅由方法说明符构成

//创建委托并保存引用
delVar = myInstObj.MyM1;
dVar = SClass.OtherM2;

(二)使用委托

下面的代码段说明了如何使用委托。这是在int上调用 ToString 方法的一种相当冗长的方式:
private delegate string GetAString();
static void Main()
(int x = 40;GetAstring firstStringMethod = new GetAString(x.Tostring);Console.WriteLine("string is (0)", firstStringMethod());// With firststringMethod initialized to x.Tostring();// the above statement is equiva1ent to saying;// Console.WriteLine("string is {0}",x.ToString());
)

这段代码中,实例化了类型为GetAString的一个委托,对它进行初始化,使它引用整型变量x的 ToString()方法。在 C++中 ,委托在语法上是接受一个参数的构造函数,个参数就是委托引 用的方法。这个方法必须匹配最初定义委托时的签名。所以在这个示例中,如果用不带参数并返回 一个字符串的方法来初始化firstStringMethod变量,会产生一个编译错误。注意,因为int.ToString()是一个实例方法(不是静态方法),所以需要指定实(x)和方法名来正确地初始化委托。

下一行代码使用这个委托来显示字符串。在任何代码中,都应提供委托实例的名称,面的圆括号中应包含调用该委托中的方法时使用的任何等效参数。所以在上面的代码中Console.WriteLine() 语句完全等价于注释语句中的代码行。

四、简单的委托示例

在这个示例中,定义一个类MathsOperations,它有两个静态方法,对double类型的值执行两个操作,然后使用该委托调用这些方法。

class MathOperations
{public static double MultiplyByTwo(double value){return value * 2;}public static double Square(double value){return value * value;}
}

调用这些方法:

using System;
namespace wrox.ProCSharp.Delegates
{delegate double DoubleOp(double x);class Program{static void Main(){Double[] operations = {MathOperation.MultiplyByTwo,MathOperations.Square};for(int i=0; i < operations.Length; i++){Console.WriteLine("Using operations[(0)]:", i);ProcessAhdDisplayNumber(operations[i], 2.0);ProcessAndDisp1ayNumber(operations[i], 7.94;ProcessAndDisplayNumber(operations[i], 1.414);Console.WriteLine();}}static void ProcessAndDisplayNumber(DoubleOP action, double value){double result = action(value);Console.WriteLine("Value is {0}, result of operation is {1}", value, result);}}
}

在这段代码中,实例化了一个委托数组DoubleOp(记住,一旦定义了委托类,基本上就可以实例化它的实例,就像处理一般的类那样——所以把一些委托的实例放在数组中是可以的)。该数组的每个元素都初始化为由MathOperations类实现的不同操作。然后遍历这个数组,把每个操作应用到3个不同的值上。这说明了使用委托的一种方式——把方法组合到一个数组中来使用,这样就可以再循环中调用不同的方法。

五、Action[T]和Func[T]委托

除了为每个参数和返回类型定义一个新类型之外,还可以使用Action<T>和Func<T>委托。

泛型Action<T>委托表示引用一个void返回类型的方法。因为这个委托类存在不同的变体,所以可以传递至多16种不同的参数类型。没有泛型参数的Action类可调用没有参数的方法。Action<in T>调用带一个参数的方法,Action<in T1,in T2>调用带两个参数的方法,Action<in T1,in T2......in T8>调用带8个参数的方法。

Func<T>委托可以以类似的方式使用。Func<T>允许调用带返回类型的方法。与Action<T>类似,Func<T>也定义了不同的变体,至多也可以传递16个参数类型和一个返回类型。Func<out TResult>委托类型可以调用带返回类型且无参数的方法,Func<in T,out TResult>调用带一个参数的方法,Func<in T1,in T2,in T3,in T4,out Tresult>调用带4个参数的方法。

六、多播委托

前面使用的每个委托都只包含一个方法调用。调用委托的次数与调用方法的次数相同。如果要调用多个方法,就需要多次显式调用这个委托。但是,委托也可以包含多个方法。这种委托称为多播委托。如果调用多播委托,就可以按顺序连续调用多个方法。为此委托的签名就必须返回void,负责就只能得到委托调用的最后一个方法的结果。

可以使用返回类型为void的Action<double>委托:

class Program
{static void Main(){Action<double> operations = MathOperations.MultiplyByTwo;operations += MathOperations.Square;}
}

因为要存储对两个方法的引用,所以实例化了一个委托数组。而这里只有在同一个多播委托中添加两个操作。多播委托可以识别运算符“+”和“+=”。还可以扩展上述代码中的最后两行:

Action<double> operation1 = MathOperations.MultiplyByTwo;
Action<double> operation2 = MathOperations.Square;
Action<double> operations = operation1 + operation2;

多播委托还能识别运算符“-”和“-=”,以从委托中删除方法的调用。

七、匿名方法

 到目前为止,要想使委托工作,方法必须已经存在(即委托是用它将调用的方法的相同签名定义的)。但还有另外一种使用委托的方法:即通过匿名方法。匿名方法是用作委托的参数的一段代码。

用匿名方法定义委托的语法与前面的定义并没有区别。但在实例化委托时,就有区别了。

using System;namespace Wrox.ProCSharp.Delegates
{class Program{static void Main(){string mid = ",middle part,";Func<String, String> anonDel = delegate(string param){param += mid;param += "and this was added to the string.";return param;};Console.WriteLine(anonDel("Start of string"));}}
}

Func<string, string>委托接受一个字符串参数,返回一个字符串。annonDel是这种委托类型的变量。不是把方法名赋予这个变量,而是使用一段简单的代码:他前面是关键字delegate,后面是一个字符串参数。

可以看出,该代码块使用方法级的字符串变量mid,变量是在匿名方法的外部定义的,把它添加到要传递的参数中。接着代码返回该字符串值。在调用委托时,把一个字符串作为参数传递, 将返回的字符串输出到控制台上。

匿名方法的优点是减少了要编写的代码。不必定义仅由委托使用的方法。这有助于降低代码的复杂性,尤其是定义了好几个事件时,代码会显得比较简单。

在使用匿名方法时,必须遵循两条规则。在匿名方法中不能使用跳转语句break、 goto或 continue) 跳到该匿名方法的外部,反之亦然:名方法外部的跳转语旬不能跳到该匿名方法的内部。 在匿名方法内部不能访问不安全的代码。另外,也不能访问在匿名方法外部使用的 ref和 out参数。但可以使用在匿名方法外部定义的其他变量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/6094.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ONNX Runtime 加速深度学习(C++ 、python)详细介绍

ONNX Runtime 加速深度学习(C 、python)详细介绍 本文在 https://blog.csdn.net/u013250861/article/details/127829944 基础上进行了更改&#xff0c;感谢原作&#xff01; ONNXRuntime(Open Neural Network Exchange)是微软推出的一款针对ONNX模型格式的推理框架&#xff0c…

妙记多 Mojidoc PC端(Mac 端+windows端)Beta版本正式上线!

你们呼唤了无数次的妙记多 Mojidoc PC客户端 Beta版本正式上线啦&#xff01; 感谢300位妙友积极参与内测&#xff0c;给予了我们很多非常有效的意见和建议&#xff01;我们会根据用户反馈不断优化和修复相关功能&#xff0c;在此感谢妙友们一直以来的支持&#xff5e; PC端拥…

SkyWalking链路追踪中span全解

基本概念 在SkyWalking链路追踪中&#xff0c;Span&#xff08;跨度&#xff09;是Trace&#xff08;追踪&#xff09;的组成部分之一。Span代表一次调用或操作的单个组件&#xff0c;可以是一个方法调用、一个HTTP请求或者其他类型的操作。 每个Span都包含了一些关键的信息&am…

numpy 笔记 partition和

1 partition 对数组进行部分排序&#xff0c;返回的是数组本身的部分排序结果 1.1 使用方法 numpy.partition(a, kth, axis-1, kindintroselect, orderNone) 1.2 参数说明 a输入的数组kth分割点&#xff0c;前kth的元素会按照部分排序的顺序排列&#xff0c;而在kth之后的…

小程序 methods方法互相调用 this.onClickCancel is not a function

背景 做了一个自定义的弹出对话窗口&#xff0c;主要是自定义一些文本颜色。 问题 但是点击按钮事件&#xff1a;取消与确认&#xff0c;调用了同一个接口&#xff0c;然后想着走不同方法&#xff0c;需要调用methods其他方法。然后报错了&#xff1a; VM1081 WAService.js:…

行为型模式 - 状态模式

概述 【例】通过按钮来控制一个电梯的状态&#xff0c;一个电梯有开门状态&#xff0c;关门状态&#xff0c;停止状态&#xff0c;运行状态。每一种状态改变&#xff0c;都有可能要根据其他状态来更新处理。例如&#xff0c;如果电梯门现在处于运行时状态&#xff0c;就不能进…

C语言数据在内存中的存储

目录 前言 本期内容介绍 一、数据类型的介绍 1.1类型的意义&#xff1a; 1.2C语言中是否有字符串类型&#xff1f; 1.3类型的基本归类 整型家族&#xff1a; 浮点型&#xff08;实型&#xff09;家族&#xff1a; 构造&#xff08;自定义&#xff09;类型&#xff1a;…

STM32外设系列—TB6612FNG

本文涉及到定时器和串口的知识&#xff0c;详细内容可见博主STM32速成笔记专栏。 文章目录 一、TB6612简介二、TB6612使用方法2.1 TB6612引脚连接2.2 控制逻辑2.3 电机调速 三、实战项目3.1 项目简介3.2 初始化GPIO3.3 PWM初始化3.3 电机控制程序3.4 串口接收处理函数 一、TB66…

优化transformer

使用transformer而导致的时间长&#xff0c;可能会由于self-attention计算Query和key的值才导致的时间长&#xff0c;也可能会因为feed forward中的计算导致时间长。这里我们只针对第一种情况下进行优化。 第一种情况&#xff1a;有些问题&#xff0c;我们可能不需要看整个句子…

【hadoop】使用Java API 上传下载数据

使用Java API 上传下载数据 使用Java API上传数据到HDFSHDFS数据的下载 使用Java API上传数据到HDFS Test public void test1() throws Exception {//构造一个输入流&#xff0c;代表要上传的数据InputStream input new FileInputStream("d:\\temp\\hadoop-2.7.3.tar.gz…

HTML <output> 标签

实例 执行计算然后在 <output> 元素中显示结果: <form οninput="x.value=parseInt(a.value)+parseInt(b.value)">0<input type="range" id="a" value="50">100+<input type="number" id="b&q…

【问题总结】基于docker-compose实现nginx转发redis

目录&#xff1a; 文章目录 需求简介&#xff1a;Q1: nginx的http模块和http模块有什么不同Q2: 可以都使用stream模块进行配置吗 Docker环境下如何转发1 修改docker-compose2 修改nginx.conf3 测试连接 需求简介&#xff1a; 需要在192.168.3.11的ngnix上&#xff0c;转发192.…

前端 | ( 十)HTML5简介及相关新增属性 | 尚硅谷前端html+css零基础教程2023最新

学习来源&#xff1a;尚硅谷前端htmlcss零基础教程&#xff0c;2023最新前端开发html5css3视频 系列笔记&#xff1a; 【HTML4】&#xff08;一&#xff09;前端简介【HTML4】&#xff08;二&#xff09;各种各样的常用标签【HTML4】&#xff08;三&#xff09;表单及HTML4收尾…

MyBatis学习笔记——4

MyBatis学习笔记——4 一、MyBatis的高级映射及延迟加载1.1、多对一1.1.1、第一种方式&#xff1a;级联属性映射1.1.2、第二种方式&#xff1a;association1.1.3、第三种方式&#xff1a;分步查询 1.2、一对多1.2.1、第一种方式&#xff1a;collection1.2.1、第二种方式&#x…

Web后端开发总结

后端web开发大致流程 和对应的核心技术 对应技术的来源 springMVC可以理解为spring框架中的web开发框架 springMVCSpringMybatis就是我们熟知的ssm框架了

golang单元测试及mock总结

文章目录 一、前言1、单测的定位2、vscode中生成单测 二、构造测试case的注意事项1、项目初始化2、构造空interface{}3、构造结构体的time.Time类型4、构造json格式的test case 三、运行单测文件1、整体运行单测文件2、运行单个单测文件报错&#xff08;1&#xff09;command-l…

基于sklearn计算precision、recall等分类指标

文章目录 一、分类指标函数1.1 precision_score函数1.2 recall_score函数1.3 accuracy_score函数1.4 f1_score函数1.5 precision_recall_curve函数1.6 roc_curve函数1.7 roc_auc_score函数1.8 classification_report函数 二、二分类任务三、多分类任务3.1 Macro Average&#x…

怎样原生制作lis的CentOS容器镜像

本文介绍从一个空白的裸机CentOS自己构造检验允许的docker环境。来达到运行环境的高度定制&#xff0c;而不是只能依赖VS或者微软或者数据库厂商提供的镜像当做基础制作。更容易理解基础原理。最终输出产物为lisnew.tar&#xff0c;一个开箱即用的lis运行环境。 制作的整个过程…

tkinter制作任意图形窗口

import tkinter from PIL import Image, ImageTkdog tkinter.Tk() # 设置图片描绘的坐标&#xff0c;注意乘号是字母x dog.geometry(500x500200100) # 不允许修改大小 dog.resizable(False, False) # 不显示标题栏 dog.overrideredirect(True) # 设置白色透明色&#xff0c;这…

os.environ[“CUDA_VISIBLE_DEVICES“]学习总结

今天发现一个很有意思的东西 import torch import os # Specify the GPU device os.environ["CUDA_VISIBLE_DEVICES"] "1" print(torch.cuda.is_available())但是如果修改下面的设置后&#xff0c;结果就变成了 import torch import os # Specify the…