首页 > 编程语言 >第壹章第14节 C#和TS语言对比-委托事件(仅C#)

第壹章第14节 C#和TS语言对比-委托事件(仅C#)

时间:2024-06-12 19:59:34浏览次数:25  
标签:委托 C# void TS number int num 第壹章 public

水一篇,因为《函数方法》章节已经说了,但那个章节比较长,知识点又多,可能有人会看不到。委托事件是C#中的一个难点,但我觉得,和TS/JS中的函数表达式放在一起时,委托和事件就变得很简单了。

一、从TS的函数表达式说起

TS/JS中函数是一等公民,function是一种类型,定义的具体函数是一个值,所以可以作为方法的参数和返回值进行传递。在C#中,函数不是一种类型,也不是一个值,需要通过委托来实现类似功能。如果从TS的角度来理解委托,委托其实很简单。所以,先从TS/JS的函数表达式说起。

//1、函数表达式的定义=================================================================
//通过类型推断来声明
let sum = function(x:number,y:number):number{
    return x + y;
}

//先定义再赋值
let sum1: (x:number,y:number) => number
sum1 = function(x:number,y:number):number{
    return x + y;
}

//也可以通过type约束
type Sum = (x:number,y:number) => number
let sum2:Sum = function(x:number,y:number):number{
    return x + y;
}

//使用箭头函数来简化定义
let sum = (x: number, y: number): number => x + y;


//2、函数作为参数使用================================================================
// 使用箭头函数定义一个函数,返回值为将参数加 1。函数定义建议使用const
const addOne = (num: number): number => num+1;

// 定义一个函数,func参数的类型是函数类型
function applyFunction(func: (num: number) => number, num: number): number {
  return func(num);
}

// 调用 applyFunction 函数,传递 addOne 函数和 5 作为参数
const result = applyFunction(addOne, 5);
console.log(result); 

二、C#中的委托和事件

2.1 C#委托的演变

//1、使用最早版本的委托==============================================================
//自定义一个委托类型(类似自定义一个类),可以认为是一个方法的模型
//以此模型创建的实例对象,参数和返回值的类型要保持一致
delegate int DeleSum(int x); //委托类型DeleSum,模型要求:1个int类型参数,返回int类型

public class Program
{
    public static void Main(string[] args)
    {   
        //创建一个委托对象,并使用AddOne方法赋值
        //类似于TS中的const deleSum = function(int x){return x+1};
        //下行代码可以简写为:DeleSum deleSum = AddOne
        DeleSum deleSum = new DeleSum(AddOne);
        
        //先简单的用一下,调用一下deleSum
        deleSum(1); //结果为2

        //复杂点,实现将AddOne方法作为参数传入ApplyFunction
        ApplyFunction(deleSum,2); //结果为3
    }

    public int AddOne(int x)
    {
        return x + 1;
    }
  
    public int ApplyFunction(DeleSum func, int num)
    {
        return func(num);
    }
}


//2、使用匿名函数和箭头函数,将定义方法的环节干掉=====================================
//上面我们创建委托对象时,还需要先定义方法AddOne,再给委托对象赋值,下面开始简化
//第一步简化,匿名函数。实现不需要定义AddOne方法
DeleSum deleSum = delegate (int x){return x+1};
//第二步简化,箭头函数。实现不用delegate关键词
DeleSum deleSum = (int x) => {return x+1};
//第三步简化,再次简化箭头函数
DeleSum deleSum = (int x) => x+1;


//3、使用内置的泛型委托类型,将定义委托类型的环节干掉=================================
//上面我们仍然要自定义一个委托类型,然后再创建委托对象
//C#内置了几个泛型的委托类型,我们不需要自定义委托类型,就可以直接使用
Func<int,int> deleSum = (int x) => x+1;
//内置的泛型委托类型主要有Action<T1,T2...>和Func<T1,T2...TResult>
//Action没有返回值,Func的最后一个泛型参数是返回值,入参可以有任意个(好像是0-16个)
//有了Action和Func,基本上不用自己再定义委托类型


//4、最后对比一下,体会一下C#和TS有什么不一样========================================
//TS
let sum = (int x,int y):int => x + y;
//C#
Func<int,int,int> sum =  (int x,int y) =>  x + y;
var a = (int x, int y) => x + y; //利用类型推断


//5、模仿泛型委托,在TS中实现类似功能,使用起来也很爽=================================
type Action = ()=>void;
type Action<T1> = (t1:T1)=>void;
type Action<T1, T2> = (t1:T1, t2:T2)=>void;
......
type Func<TRusult> = ()=>TRusult;
type Func<T1, TRusult> = (t1:T1)=>TRusult;
type Action<T1, T2, TRusult> = (t1:T1, t2:T2)=>TRusult;
......
import type { Action, Func } from './types';

function demo(func:Func<number,bool>, num:number){
  func(num);
}
const f1 = (a:number):bool=>{
  a>0 ? true : false;
}
demo(f1,10);

2.2 更加强大的C#委托

经过一系列骚操作,C#委托在形式上,已经无限接近灵活的函数表达式。但本质上,两者还是不一样的东西,而且C#的委托拥有更加强大的功能。

2.2.1 多播委托
//多播委托可以实现多个方法的代理,调用委托变量时,多个方法按赋值的顺序调用
class Program
{
    static void Main()
    {
        Action<string> multicastDelegate = null;

        multicastDelegate += PrintMessage1;
        multicastDelegate += PrintMessage2;

        //执行委托对象multicastDelegate后,两个方法都会被调用
        multicastDelegate("Hello!");
    }

    static void PrintMessage1(string message)
    {
        Console.WriteLine("From PrintMessage1: " + message);
    }

    static void PrintMessage2(string message)
    {
        Console.WriteLine("From PrintMessage2: " + message);
    }
}

2.2.2 事件

事件是基于委托实现的,利用多播委托的特性,在委托的基础上,又抽象的一层。

//1、委托和事件的关系===============================================================
//下例是一个事件定义和触发的简单案例,一开始会感觉和委托没啥区别
//再领会,会发现事件实现了订阅和触发的分离,在此基本上,实现了GUI的事件机制
//比如下例中的EventClass,你可以认为它是一个Button,定义了点击事件
//在Program中,创建了Button对象,然后订阅事件回调,最后触发事件

//1.1 定义委托类型----------------------------------------
delegate void MyDelegate(int num);

//1.2 定义包含事件的类------------------------------------
class EventClass
{
    //事件是类的成员,使用event修饰词,类型必须是委托类型
    public event MyDelegate MyEvent;

    public void TriggerEvent(int num)
    {
        MyEvent?.Invoke(num); //定义触发事件的方法
    }
}

//1.3 订阅事件和触发事件-----------------------------------
class Program
{
    static void Main()
    {
        EventClass eventClass = new EventClass();//实例化包含事件的类
        
        eventClass.MyEvent += EventHandler1; //订阅事件回调1
        eventClass.MyEvent += EventHandler2; //订阅事件回调2
      
        eventClass.TriggerEvent(10); //触发事件
    }
  
    //事件回调,触发/发布事件时,执行的业务逻辑。还需要订阅绑定才能生效。
    static void EventHandler1(int num)
    {
        Console.WriteLine("通过事件调用 EventHandler1: " + num);
    }
    static void EventHandler2(int num)
    {
        Console.WriteLine("通过事件调用 EventHandler2: " + num);
    }
}



//2、实际使用过程中使用框架内置的委托类型============================================
//2.1 EventHandler--------------------------------
//EnentHander:public delegate void EventHandler(object sender, EventArgs e);
class Program
{
    static void Main()
    {
        MyEventSource eventSource = new MyEventSource();
        eventSource.SomeEvent += EventSource_SomeEvent;
        eventSource.TriggerEvent();
    }
    static void EventSource_SomeEvent(object sender, EventArgs e)
    {
        Console.WriteLine("SomeEvent 发生了!");
    }
}
//非泛型事件类
class MyEventSource
{
    public event EventHandler SomeEvent;
    public void TriggerEvent()
    {
        SomeEvent?.Invoke(this, EventArgs.Empty);
    }
}

//2.2 EventHandler<TEventArgs>-----------------------
//EventHandler只能使用预定义参数类型EventArgs,泛型版本可以自定义参数类型
/*public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e) 
                                                where TEventArgs : EventArgs;
*/
class Program
{
    static void Main()
    {
        MyEventWithArgs eventSource = new MyEventWithArgs();
        eventSource.SomeEventWithArgs += EventSource_SomeEventWithArgs;
        eventSource.TriggerEventWithArgs(42);
    }

    static void EventSource_SomeEventWithArgs(object sender, MyEventArgs e)
    {
        Console.WriteLine($"SomeEventWithArgs 发生了,参数值: {e.Value}");
    }
}
//泛型事件类
class MyEventWithArgs
{
    public event EventHandler<MyEventArgs> SomeEventWithArgs;
    public void TriggerEventWithArgs(int value)
    {
        SomeEventWithArgs?.Invoke(this, new MyEventArgs(value));
    }
}
//自定义类型参数
class MyEventArgs : EventArgs
{
    public int Value { get; set; }
    public MyEventArgs(int value)
    {
        this.Value = value;
    }
}

标签:委托,C#,void,TS,number,int,num,第壹章,public
From: https://blog.csdn.net/2401_85195613/article/details/139635205

相关文章

  • 线程安全问题【snychornized 、死锁、线程通信】
    目录一、线程安全1.1线程安全问题?1.2如何解决线程安全问题方法具体如何实现?1.3同步方法1.4同步代码块1.5总结1.6售票例子1.8补充二、线程安全的集合三、死锁【了解】四、线程通信4.1同步方法4.2同步代码块4.3wait和sleep本篇的思维导图最后一、线程......
  • Java日期类Date、SimpleDateFormat 日期格式类、Calendar详细介绍
    目录一、Date类1.1Date类简单介绍1.2Date类的构造方法代码演示二、SimpleDateFormat日期格式化类2.1SimpleDateFormat日期格式化类简单介绍2.2构造方法代码演示日期格式化模板常用方法代码演示注意三、Calendar类3.1简单介绍3.2创建对象代码演示3.3静......
  • 设计一个程序,作为进程A,进程A专门创建一个信号量集,要求信号量集中有1个信号量,对信号量
    设计一个程序,作为进程A,进程A专门创建一个信号量集,要求信号量集中有1个信号量,对信号量集合中的信号量进行设置,要求集合中的信号量的初值为1,然后再设计2个程序,分别是进程B和进程C,要求进程B和进程C使用进程A创建的信号量集合中的信号量实现互斥访问。提示:进程A、进程B、进程C需要使......
  • 【jmeter】ubuntu分布式jmeter报错:java.rmi.ConnectException: Connection refused to
    一、场景   由于高并发测试,服务器资源不够用,所以需要使用jmeter分布式进行测试,但是测试过程中报错:java.rmi.ConnectException:Connectionrefusedtohost:127.0.1.1;  二、问题原因   就是hostname-i如果返回的是127.0.1.1 三、处理方法   修改hostna......
  • Neural machine translation of rare words with subword units
    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! Abstract 1Introduction  2NeuralMachineTranslation 3SubwordTranslation 3.1RelatedWork 3.2BytePairEncoding(BPE) 4Evaluation 4.1Subwordstatistics 4.2Translation......
  • 【redis】使用redis benchmark评估哨兵模式主节点性能
    一、场景   验证redis哨兵模式主节点性能 二、工具Redis benchmark官网Redisbenchmark|Docs 三、命令介绍 四、使用redis-benchmark-h192.168.3.190-p26380-a123456-n100000-c20======PING_INLINE======100000requestscompletedin1.4......
  • mac 微信、QQ备份到外接硬盘方案(软链接)
    一、微信备份到外接硬盘mac微信备份到外接硬盘方案(软链接)要找到mac版微信的缓存文件很简单。因为它缓存的文件都保存在电脑的【~/Library/Containers/com.tencent.xinWeChat/Data/Library/ApplicationSupport/com.tencent.xinWeChat/2.0b4.0.9】这个路径中。我们只需要......
  • 借助ChatGPT“从0到1”高效完成优质论文写作,4大关键步骤(技巧+实例),小白轻松上手
    最近很多朋友找我咨询ChatGPT写论文的事,说他们虽然知道GPT很厉害,但是却不知道怎么上手使用,有的是使用了但并没有那么理想,对于AI输出的结果并不满意,从零开始撰写论文似乎是一项艰巨的工作。我将通过这篇文章介绍如何借助ChatGPT从0到1一步一步完成从文献调研到论文发表的全过......
  • 栈溢出漏洞利用二,ret2syscall,构造rop链条实现攻击(pwn入门)
    原理原理就直接参考别的大佬写的文章讲下了 参考文章:https://blog.csdn.net/qq_33948522/article/details/93880812ret2syscall,即控制程序执行系统调用,获取shellret2syscall通常采用execve(重点函数,32位调用号为0x0b,64位调用号为0x3b)ROPReturnOrientedProgramming,其......
  • ThreadLocal源码分析
    目录0x00ThreadLocal0x01ThreadLocalMap0x02ThreadLocal内存泄漏0x00ThreadLocalThreadLocal提供了线程局部的变量,但和普通局部变量不同,同一个ThreadLocal变量可以被多个线程共享,而不是线程私有的。在ThreadLocal源代码中有一个使用例子,代码如下:importjava.ut......