C#泛型委托

在C#中,delegate 关键字用于声明委托(delegates),委托是一种类型安全的函数指针,允许你传递方法作为参数或从方法返回方法。有时我们需要将一个函数作为另一个函数的参数,这时就要用到委托(Delegate)机 制。

delegate void GDelegate<T>(T t);
定义了一个名为 GDelegate 的泛型委托。这个委托接受一个类型为 T 的参数 t,并且不返回任何值(void)。T 是一个类型参数,意味着这个委托可以用于任何类型的方法,只要那个方法有一个参数并且没有返回值。

在C#中,Action 是一个内置的委托(delegate)类型,用于封装没有返回值(即返回类型为 void)的方法。Action 委托有多个重载版本,可以接受不同数量的参数,每个参数可以有不同的类型。
Action 委托的基本定义如下:
public delegate void Action(); // 没有参数  
public delegate void Action<T>(T obj); // 一个参数  
public delegate void Action<T1, T2>(T1 arg1, T2 arg2); // 两个参数  
// ... 以此类推,直到 Action<T1, T2, ..., T16>

在C#中,Func<TResult> 是一个泛型委托,用于封装具有返回值的方法。与 Action 委托不同,Func 委托总是有一个返回值,并且可以接受任意数量的输入参数。在你给出的例子中,Func<String, String> 是一个特定的 Func 委托类型,它接受一个 string 类型的参数并返回一个 string 类型的值。

Func<String,String>func=new Func<String,String>(Method3);
func("我是带返回值的Func委托"); 

Gdelegate类: 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 泛型委托
{
    delegate void GDelegate<T>(T t);//定义了一个名为 Gdelegate 的泛型委托。这个委托接受一个类型为 T 的参数 t,并且不返回任何值
                                    //T 是一个类型参数,意味着这个委托可以用于任何类型的方法,只要那个方法有一个参数并且没有返回值。
    internal class Gdelegate//internal 是一个访问修饰符,用于指定一个类型或成员仅在其声明它的程序集中可见
    {
         static string result;
        public static void InvokeDelegate() {
            GDelegate<string> gdelegate1 = new GDelegate<string>(Method1);//把方法以变量的形式传送,并以方法的形式执行
            gdelegate1("我是泛型委托1");

            Action<String>action=new Action<String>(Method1);
            action("我是泛型委托1-action");//官方版本(不带返回值),效果同
//Action<T>和Func<TResult>这两个内置的委托类型,它们分别在System命名空间中定义,用于处理没有返回值(Action)和带有返回值(Func)的方法。

            GDelegate<int> gdelegate2= new GDelegate<int>(Method2);
            gdelegate2(99);//传参int

          Func<String,String>func=new Func<String,String>(Method3);
            result =func("我是带返回值的Func委托");//Func<String, String> 是一个特定的 Func 委托类型,它接受一个 string 类型的参数并返回一个 string 类型的值。

        }
        public static void Method1(string str) {
        Console.WriteLine(str);
                }
        public static void Method2(int num)
        {
            Console.WriteLine("我是泛型委托2:"+num);
        }
        public static String Method3(string str)
        {
            Console.WriteLine(result + "AB");
            return str+"A";
           
        }

    }
}

  Program类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 泛型委托
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Gdelegate.InvokeDelegate();//调用静态方法----类名.方法名
           string str= Gdelegate.Method3("我是带参的");//str接收的是return str+"A"
            // Func 委托类型,它接受一个 string--"我是带参的" 类型的参数,并返回一个 string
            Console.WriteLine(str);
            Console.ReadKey();
        }
    }
    
}

启动程序:

AB是在执行:Gdelegate.InvokeDelegate();//调用静态方法----类名.方法名
时执行了: Func<String,String>func=new Func<String,String>(Method3);--->Console.WriteLine(result + "AB");//
而result 此时为null.下一句:result =func("我是带返回值的Func委托:");
 result接收了:"我是带返回值的Func委托:"+"A"
所以才有了执行:string str= Gdelegate.Method3("我是带参的");
时的:我是带返回值的Func委托:AAB
 执行:Console.WriteLine(str);
       我是带参的A

--------------------------------------------

下面我们设计一个马戏表演函数 CircusStart(),它的第一个参数是代表动物表演的函 数,传给它什么样的函数,就进行什么动物的表演。

c#控制台程序:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace delegateAnimalPlay
{
    internal class Program
    {//定义委托 
        delegate void AnimalPlay(string name);//委托声明 AnimalPlay
        static void Main(string[] args)
        {
           AnimalPlay deleDogPlay = new AnimalPlay(DogPlay); //把函数 DogPlay()转换为 AnimalPlay 型委托

            CircusStart(deleDogPlay, "Good evening");// 把委托 deleDogPlay 传给函数CircusStart()
            Console.ReadKey();

        }
        static void CircusStart(AnimalPlay animalPlay, string hello)//静态方法 CircusStart 的签名,该方法接受一个 AnimalPlay 委托和一个字符串参数 name
        {
            Console.WriteLine("女士们,先生们,我们的马戏表演开始了!");
            animalPlay(hello);

        }
        //函数:狗表演 
        static void DogPlay(string greetings)
        {
            Console.WriteLine("{0},I am Snoopy!", greetings);//greetings接收hello--"Good evening,I am Snoopy!"
            Console.WriteLine(@"  狗在表演_");
        }
        //函数:猫表演 
        static void CatPlay(string greetings)
        {
            Console.WriteLine("{0},I am Kitty!", greetings);
            Console.WriteLine(@" 猫在表演");
        }
        //函数:狮子表演 
        static void LionPlay(string greetings)
        {
            Console.WriteLine("{0},I am Simba!", greetings);
            Console.WriteLine(@"狮子在表演 ");

    
        }  
}
}

启动程序: 

委托实例deleDogPlay实际上相当于函数DogPlay()的别名。 我们也可以传入CatPlay(),看看结果

在我们的程序中,我们总是调用函数 CircusStart()进行表演,定义该函数时,我们不 知道也不关心传递给它的委托到底代表哪个函数,直到调用函数 CircusStart(),并把实际 参数传递给它时,这个函数才具体化。传给它什么样的具体函数,就进行什么样的表演, 传给它 deleDogPlay,马戏团就进行狗的表演;传给它 deleCatPlay,马戏团就进行猫的表 演;传给它 deleLionPlay,马戏团就进行狮子表演。因此以委托为参数的函数具有一定的 通用性。 

------------------------------------

下面我们利用委托的通用性设计一个通用的求定积分的函数。

函数f(x)在区间[a,b]上定积分  \int_{a}^{b}  f x ( ) 等于函数图像与x轴所围成的曲边梯形的面积。

怎样求曲边梯形的面积呢?我们把曲边梯形分成无数块细小的矩形,这些小矩形面积 之和即可以看做曲边梯形面积的近似值。显然小矩形分得越细,面积就越精确。假如我们 把它分成1000份,则每个小矩形的宽度为:\Delta = \frac{b-a}{1000}

第i个小矩形的宽为Δ,高为f(a+iΔ),所以其面积为:Si=Δ*f(a+iΔ)

故函数f(x)在区间[a,b]上的定积分(曲边梯形的面积)为:\int_{a}^{b}  f x ( )=\sum_{i=1}^{1000}Si=\sum_{i=1}^{1000}Δ*f(a+iΔ)

求定积分,需要知道积分上限a,积分下限b和被积函数f(x),需要把被积函数以参数的形式传递给定积分函数,所以需要利用委托实现。 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace delegateIntegral
{
    internal class Program
    { //被积函数的委托 
        delegate double Integrand(double x);

        static void Main(string[] args)//进行定积分运算
        {
            double result1 = DefiniteIntegrate(1, 5, F1);
            double result2 = DefiniteIntegrate(0, 1, F2);

            Integrand f3 = delegate (double x)
            {
                return 3 * x + 5;
            };
            double result3 = DefiniteIntegrate(2, 8, f3);//匿名函数当作函数的参数
            Console.WriteLine("result3 = {0}", result3);


            Console.WriteLine("result1 = {0}", result1);
            Console.WriteLine("result2 = {0}", result2);
            Console.ReadKey();
        }

        //被积函数F1(x)=2x+1 
        static double F1(double x)
        {
            return 2 * x + 1;
        }
        //被积函数F2(x)=x2 
        static double F2(double x)
        {
            return x * x;
        }
       
        //函数:定积分 
        static double DefiniteIntegrate(double a, double b, Integrand fun)
        {
            const int sect = 1000;
            //分割数目 
            double delta = (b - a) / sect;
            double area = 0;
            for (int i = 1; i <= 1000; i++)
            {
                area += delta * fun(a + i * delta);
            }

            return area;
        }


    }
}

运行程序:

利用委托可以实现以函数为参数,提高 程序的通用性。实际上委托也是由类实现的,当我们创建一种委托时,.NET会创建一个从System.Delegate派生出来的类,类中有一个调用列表, 列表中包含着指向被委托函数的引用。学过C++的读者会觉得委托与C++的函数指针非常 类似,然而与C++的函数指针相比,委托是一种类型安全的方式,并能实现很多其它功能。

创建委托实例时不仅可以使用已有的函数,而且可以直接使用匿名函数(Anonymous Function)。

static void Main(string[] args) 

    / / 匿名函数 
    Integrand f3 = delegate(double x) 
    { 
        return 3 * x + 5; 
    } ; 
    double result3= DefiniteIntegrate(2, 8, f3); 
    Console.WriteLine("result3 = {0}", result3); 

}

也可以直接把匿名函数当作函数的参数。 double result3 = DefiniteIntegrate(2,8, delegate(double x){ return 3*x+5;});

利用匿名函数可以直接把“代码块”定义成委托,而不需要事先定义函数。在上面的 代码中我们定义了一个Integrand型委托f3,该委托的具体代码在后面的花括号中(注意, 花括号后要添加分号)。匿名函数有很多优点,比较突出的一个是它不光可以使用代码块 内定义的变量,而且可以使用代码块外定义的变量,即可以使用宿主函数的局部变量。比如下面的代码: 
static void Main(string[] args) 

double a = 3; 
double b = 5; 
    Integrand f4 = delegate(double x) 
    { 
        return a * x + b; 
    } ; 
double result4 = DefiniteIntegrate(2, 8, f4); 
    Console.WriteLine("result4 = {0}", result4);
}

在上面的代码中,在匿名函数内部使用了外部定义的变量a、b,我们把这种情况称为 外层变量被匿名函数捕获,它们的生存周期将延长至委托被销毁为止。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java项目之车辆管理系统(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的车辆管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 车辆管理系统的主要使用者分…

Deckset for Mac:让演示文稿制作更轻松

还在为繁琐的演示文稿制作而烦恼吗&#xff1f;Deckset for Mac来帮您解决&#xff01;它支持Markdown语言&#xff0c;让您只需专注于内容的创作&#xff0c;无需在排版和设计上耗费过多精力。丰富的主题和布局选项&#xff0c;让您能够轻松打造出专业级的演示文稿。快来体验D…

云计算第十二课

安装虚拟机 第一步新建虚拟机 选择自定义安装 下一步 选择稍后安装操作系统 选择系统类型和版本 选择虚拟机文件路径&#xff08;建议每台虚拟机单独存放并且路径不要有中文&#xff09;点击下一步 选择bios下一步 选择虚拟机处理器内核数量 默认硬盘或者自行调大硬盘 选择虚…

软件测试的分类

1.用户分类 2.查看代码分类 3.阶段分类

云计算十三课

centos安装 点击左上角文件 点击新建虚拟机 点击下一步 点击稍后安装操作系统&#xff0c;下一步 选择Linux&#xff08;l&#xff09;下一步 设置虚拟机名称 点击浏览选择安装位置 新建文件夹设置名称不能为中文&#xff0c;点击确定 点击下一步 设置磁盘大小点击下一步…

4.1 编写程序,从键盘接收一个小写字母,然后找出他的前导字符和后续字符,再按顺序显示这三个字符

方法一&#xff1a; 运行效果&#xff1a; 输入B&#xff0c;输出显示ABC&#xff1b;输入A&#xff0c;输出显示AB 思路&#xff1a; 1、通过键盘输入接收一个字母。 2、将输入的字母减去1&#xff0c;得到前导字符&#xff0c;然后输出。 3、将输入的字母加上1&#xff0c;得…

【python量化交易】qteasy使用教程07——创建更加复杂的自定义交易策略

创建更加复杂的自定义交易策略 使用交易策略类&#xff0c;创建更复杂的自定义策略开始前的准备工作本节的目标继承Strategy类&#xff0c;创建一个复杂的多因子选股策略策略和回测参数配置&#xff0c;并开始回测 本节回顾 使用交易策略类&#xff0c;创建更复杂的自定义策略 …

(四十)第 6 章 树和二叉树(树的双亲表存储)

1. 背景说明 2. 示例代码 1) errorRecord.h // 记录错误宏定义头文件#ifndef ERROR_RECORD_H #define ERROR_RECORD_H#include <stdio.h> #include <string.h> #include <stdint.h>// 从文件路径中提取文件名 #define FILE_NAME(X) strrchr(X, \\) ? strrch…

基于yolov5+streamlit目标检测演示系统设计

YOLOv5与Streamlit&#xff1a;智能目标检测可视化展示介绍 随着人工智能技术的飞速发展&#xff0c;目标检测技术已成为推动智能化社会进步的关键技术之一。在众多目标检测算法中&#xff0c;YOLOv5以其卓越的性能和实时性&#xff0c;成为了业界的佼佼者。与此同时&#xff…

代码随想录阅读笔记-动态规划【爬楼梯】

题目 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 注意&#xff1a;给定 n 是一个正整数。 示例 1&#xff1a; 输入&#xff1a; 2输出&#xff1a; 2解释&#xff1a; 有两种方法可以爬到楼…

[AutoSar]BSW_Diagnostic_002 DCM模块介绍

目录 关键词平台说明背景一、DCM所处架构位置二、DCM 与其他模块的交互三、DCM 的功能四、DCM的内部子模块4.1 Diagnostic Session Layer (DSL)4.1 DSL 与其他模块的交互 4.2 Diagnostic Service Dispatcher (DSD)4.3 Diagnostic Service Processing (DSP)4.4 小结 关键词 嵌入…

vue3土味情话pinia可以持久保存再次修改App样式

我是不是你最疼爱的人-失去爱的城市 <template><div class"talk"><button click"getLoveTalk">土味情话</button><ul><li v-for"talk in talkStore.talkList" :key"talk.id">{{ talk.title }}<…

计算机服务器中了360后缀勒索病毒怎么解密,360后缀勒索病毒恢复

计算机网络技术的不断发展与应用&#xff0c;为企业的生产运营提供了极大便利&#xff0c;大大提高了企业的办公效率&#xff0c;为企业的生产运营注入了新的动力&#xff0c;但网络是一把双刃剑&#xff0c;在为企业提供便利的同时&#xff0c;也为企业的数据安全带来严重威胁…

macos使用yarn创建vite时出现Usage Error: The nearest package directory问题

步骤是macos上使用了yarn create vite在window上是直接可以使用了yarn但是在macos上就出现报错 我们仔细看&#xff0c;它说的If /Users/chentianyu isnt intended to be a project, remove any yarn.lock and/or package.json file there.说是要我们清除yarn.lock和package.js…

深圳晶彩智能ESP32-1732S019实时观看GPIO的状态

深圳晶彩智能ESP32-1732S019介绍 ESP32-1732S019开发板是基于ESP32-S3-WROOM-1模块作为主控&#xff0c;双核MCU ,集成WI-FI和蓝牙功能&#xff0c;主控频率可达240MHz , 512KB SRAM , 384KB ROM&#xff0c;8M PSRAM&#xff0c;16MB Flash&#xff0c;显示分辨率为170*320 I…

职校智慧校园现状及问题分析

各大中职院校及高职院校是校园信息化的先行者和开拓者&#xff0c;很早就开始注重信息化基础设施建设和信息化人文素养的提升。在过去几年里&#xff0c;随着国家大力发展与扶植职校教育&#xff0c;学校投入相当的经费进行了校园信息通信网络、计算机等基础硬件设备建设&#…

单调栈问题

原理 单调栈的核心原理是&#xff1a;在栈内保持元素的单调性&#xff08;递增或递减&#xff09; 单调递增栈&#xff1a; 用于处理“下一个更小的元素”问题。当新元素比栈顶元素小或等于时&#xff0c;直接入栈&#xff1b;否则&#xff0c;一直从栈顶弹出元素&#xff0c…

AtCoder Regular Contest 177 D. Earthquakes(概率 单调栈)

题目 D - Earthquakes 思路来源 官方题解 题解 对于不存在连锁反应的区间&#xff0c;每个区间独立处理&#xff0c;最后求个乘积 对于每个区间&#xff0c;相邻的两个杆子距离都小于H&#xff0c; 意味着没倒的区间是个连续的区间&#xff0c;假设要算i的概率 一定是第i…

金三银四面试题(二十七):适配器模式知多少?

什么是适配器模式 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许将一个类的接口转换为客户期望的另一个接口。通过适配器&#xff0c;原本不兼容的接口可以一起工作&#xff0c;从而提高系统的灵活性和可扩展性。 关键元素&…

JVM 类的加载器

文章目录 1. 作用2. 类加载器的显示加载与隐式加载3. 类加载机制的必要性4. 加载的类是唯一的吗5. 类加加载机制的基本特征(了解) 1. 作用 类加载器是 JVM 执行类加载机制的前提。 ClassLoader 的作用&#xff1a; ClassLoader 是 Java 的核心组件&#xff0c;所有的 Class 都…