首页 > 编程语言 >C#:Delagate(委托)

C#:Delagate(委托)

时间:2023-09-25 23:23:19浏览次数:33  
标签:委托 C# System Delagate int Student new using

概念介绍

C++/C函数指针  

  C#委托是C/C++中 函数指针的升级版。C++函数指针本质是一串32位/64位的地址,函数指针是指向函数的指针,可以借此指针调用函数,也可以用指针指向另一个函数。函数指针举例如下:

#include <iostream>
using namespace std;

typedef int (*cal)(int a, int b);//函数指针:返回值类型+指针名称+参数列表
int Add(int a, int b);//函数声明

int main(void)
{

    cal funtion11 = &Add;//函数指针指向ADD函数,也可以指向其他函数
    int sum = funtion11(1,2);

    cout << "FUNCIOTN IS "<<sum << endl;
}

int Add(int a, int b)
{
    int result = a + b;
    return result;
}

  可以看出函数指针可以指向任何符合定义类型的函数。这是一种间接调用方式。

  直接调用:直接调用函数名。
  间接调用:通过函数函数指针调用函数。

C#委托

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

  委托的声明和调用类似于函数指针,并且C#是基于C++设计的,因此委托是函数指针的升级版。
  C#基本的委托有两种:Action、Func,这无须开发者自己定义。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Calculater Calculater = new Calculater();
            
            Action action = new Action(Calculater.Report);//Action委托指向无返回值的report方法
            action();//间接调用

            Func<int/*返回值类型*/, int/*函数参数1*/, int/*函数参数2*/> func = new Func<int, int, int>(Calculater.Add);//Function委托指向有返回值的函数
            int result = func(1,2);
            Console.WriteLine(result);
        }
    }

    class Calculater
    {
        public void Report() => Console.WriteLine("Hello world!");
        public int Add(int a, int b)
        { 
            int result =a + b;
            return result;
        }
    }
}

如何自定义委托?

  委托实际是一种类,您可以执行以下语句,结果返回True。另外,如果您使用的是VS Code开发,可以看到ActionFunc的颜色为水蓝色,这代表一种数据类型。

Type type = typeof(Action);

   既然是类,这代表开发者可以自定义委托。举例如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    public delegate int/*返回值类型*/ Calc(int a/*参数1*/, int b/*参数2*/);//自定义委托

    internal class Program
    {
        static void Main(string[] args)
        {
            Calculater Calculater = new Calculater();

            Calc calc = new Calc(Calculater.Add);//新建委托
            int res = calc(1,2);
            Console.WriteLine(res);
        }
    }

    class Calculater
    {
        public int Add(int a, int b)
        { 
            int result =a + b;
            return result;
        }
    }
}
  • 自定义委托返回的数据类型必须和指向的函数一致。
  • 自定义委托的参数个数和数据类型必须和指向的函数一致。
  • 委托的声明必须声明在名称空间中,和其他类同级。

委托的一般使用

  C#委托意义同C++函数指针的意义,一般都是将委托当作参数传递给另一个方法。有Template方法和CallBack方法两种。

Template方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    public delegate int Calc(int a, int b);//自定义委托

    internal class Program
    {
        static void Main(string[] args)
        {
            ProductFactory productFactory = new ProductFactory();//创建一个产品工厂
            WrapFactory wrapFactory = new WrapFactory();//创建一个包装工厂
            
            //创建委托
            Func<Product/*返回值类型*/> fun1 = new Func<Product>(productFactory.MakeCake/*满足返回值类型的方法*/);
            //将委托作为参数输入给包装工厂
            Box box1 =  wrapFactory.WarpProduct(fun1);//输入指向产品的委托

            Console.WriteLine( box1.Product.name);
        }
    }

    class Product//产品制作
    { 
        public string name { get; set; }
    }

    class Box//包装盒制作
    {
        public Product Product { get; set; }
    }

    class ProductFactory//蛋糕生产
    {
        public Product MakeCake()
        {
            Product product = new Product();
            product.name = "Da_Li_Yuan";
            return product;
        }
    }

    class WrapFactory //产品包装
    {
        public Box WarpProduct(Func<Product> getProduct)/*委托作为函数参数,意味着必须输入一个指向产品的委托*/
        {
            Box box = new Box();
            Product product = getProduct();//调用委托
            box.Product = product;
            return box;
        }
    }
    
}

  上述这种方法就是模板方法:调用通用的外部方法,有返回值。只要满足Func<Product> getProduct,都可以传入方法。

CallBack方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    public delegate int Calc(int a, int b);//自定义委托

    internal class Program
    {
        static void Main(string[] args)
        {
            ProductFactory productFactory = new ProductFactory();//创建一个产品工厂
            WrapFactory wrapFactory = new WrapFactory();//创建一个包装工厂
            Logger logger = new Logger();

            Func<Product/*返回值类型*/> fun1 = new Func<Product>(productFactory.MakeCake/*满足返回值类型的方法*/);
            Action<Product> log1 = new Action<Product>(logger.Log);
            Box box1 =  wrapFactory.WarpProduct(fun1,log1);//输入指向产品的委托,输入log函数

            Console.WriteLine( box1.Product.name);
        }
    }

    class Logger//记录程序的运行状态
    {
        public void Log(Product product)
        { 
            Console.WriteLine("Produce {0} created at {1},Price is {2}",product.name,DateTime.UtcNow,product.Price);
        }
    }

    class Product//产品制作
    { 
        public string name { get; set; }
        public double Price { get; set; }
    }

    class Box//包装盒制作
    {
        public Product Product { get; set; }
    }

    class ProductFactory//蛋糕生产
    {
        public Product MakeCake()
        {
            Product product = new Product();
            product.name = "Da_Li_Yuan";
            product.Price = 100;
            return product;
        }
    }

    class WrapFactory //产品包装
    {
        public Box WarpProduct(Func<Product> getProduct,Action<Product> logCallBack)/*委托作为函数参数,意味着必须输入一个指向产品的委托*/
        {
            Box box = new Box();
            Product product = getProduct();//调用委托

            if(product.Price>=50)
                logCallBack.Invoke(product);

            box.Product = product;
            return box;
        }
    }
}

  上述方法就是回调方法:调用指定的外部方法,无返回值。比如这里的log。

注意事项

  • 委托是一种方法级别的高度耦合,使用时请慎之又慎!
  • 委托使可读性降低,debug难度增加
  • 委托和多线程,异步调用,委托回调纠缠在一起时,请慎之又慎!
  • 委托使用不当将导致内存泄漏和性能下降。

委托的高级使用

多播委托

  多播委托指一个委托内部包含有多个委托/方法。单播委托指一个 委托只委托一个方法。

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

namespace AdvancedDelegate
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Student student1 = new Student() {Id = 1,Color = ConsoleColor.Yellow};
            Student student2 = new Student() { Id = 2, Color = ConsoleColor.Green };
            Student student3 = new Student() { Id = 3, Color = ConsoleColor.Red }; ;
            Action action1 = new Action(student1.DoHomeWork);
            Action action2 = new Action(student2.DoHomeWork);
            Action action3 = new Action(student3.DoHomeWork);

            //单播委托:一个委托进行执行
            action1();
            action2();
            action3();

            //多播委托:一个委托封装多个委托的方式,执行的顺序按照封装的顺序
            action1 += action2;
            action1 += action3;
            action1();
        }
    }

    public class Student
    {
        public int Id { get; set; }
        public ConsoleColor Color { get; set; }

        public void DoHomeWork()
        {
            for(int i=0;i<5;i++)
            {
                Console.ForegroundColor = this.Color;
                Console.WriteLine("Student {0} doing homework {1} hours", this.Id, i);
                Thread.Sleep(200);
            }
        }
    }
}

  多播采用+进行委托包含。

异步与同步调用

  线程与进程:一个Main函数/代码文件就是一个进程,一个进程可以包含多个进程。 

  同步调用:顺序执行,多个线程接力跑。同步=单线程=串行。线程会互相影响。
  

  异步调用:同时执行,多个线程同时跑 。异步=多线程=并行。线程不会互相影响。
  

同步调用

static void Main(string[] args)
        {
            Student student1 = new Student() {Id = 1,Color = ConsoleColor.Yellow};
            Student student2 = new Student() { Id = 2, Color = ConsoleColor.Green };
            Student student3 = new Student() { Id = 3, Color = ConsoleColor.Red }; ;
            Action action1 = new Action(student1.DoHomeWork);
            Action action2 = new Action(student2.DoHomeWork);
            Action action3 = new Action(student3.DoHomeWork);

            //直接同步调用
            student1.DoHomeWork();
            student2.DoHomeWork();
            student3.DoHomeWork();
    
            //间接同步调用(单播),单播委托就是同步调用
            action1();
            action2();
            action3();
    
            //间接同步调用(多播),多播委托也是同步调用
            action1 += action2;
            action1 += action3;
            action1();

            //主线程继续做其他的事情
            //.... ....
        }

异步调用  

  隐式异步指使用委托的Begininvoke,显式异步指Thread开辟线程。

  隐式异步调用即委托采用Delegate.BeginInvoke(AsyncCallback callback,object object)方法执行,使用此方法时程序将开辟一个线程来执行委托。

        static void Main(string[] args)
        {
            Student student1 = new Student() {Id = 1,Color = ConsoleColor.Yellow};
            Student student2 = new Student() { Id = 2, Color = ConsoleColor.Green };
            Student student3 = new Student() { Id = 3, Color = ConsoleColor.Red }; ;
            Action action1 = new Action(student1.DoHomeWork);
            Action action2 = new Action(student2.DoHomeWork);
            Action action3 = new Action(student3.DoHomeWork);

            ////隐式异步调用
            action1.BeginInvoke(null, null);
            action1.BeginInvoke(null, null);
            action1.BeginInvoke(null, null);
        }
    }

  可以看出,三个委托不会互相等待,并且打印出了不同的颜色,这是因为异步调用时三个委托都在同时访问前景色,导致颜色一致,只有采用线程锁才可以解决此问题。

  显示异步采用Thread自定义线程。

    internal class Program
    {
        static void Main(string[] args)
        {
            Student student1 = new Student() {Id = 1,Color = ConsoleColor.Yellow};
            Student student2 = new Student() { Id = 2, Color = ConsoleColor.Green };
            Student student3 = new Student() { Id = 3, Color = ConsoleColor.Red }; 

            //显示异步调用方式1
            Thread thread1 = new Thread(new ThreadStart(student1.DoHomeWork));
            Thread thread2 = new Thread(new ThreadStart(student2.DoHomeWork));
            Thread thread3 = new Thread(new ThreadStart(student3.DoHomeWork));

            thread1.Start();
            thread2.Start();
            thread3.Start();
        }
    }


  显示异步调用也可采用task进行,但是要包含using System.Threading.Tasks

    internal class Program
    {
        static void Main(string[] args)
        {
            Student student1 = new Student() {Id = 1,Color = ConsoleColor.Yellow};
            Student student2 = new Student() { Id = 2, Color = ConsoleColor.Green };
            Student student3 = new Student() { Id = 3, Color = ConsoleColor.Red }; ;

            //显示异步调用方式2
            Task task1 = new Task(student1.DoHomeWork);
            Task task2 = new Task(student2.DoHomeWork);
            Task task3 = new Task(student3.DoHomeWork);

            task1.Start();
            task2.Start();
            task3.Start();
        }
    }

  综上:

  • 直接同步调用:直接调用方法名。
  • 间接同步调用:将方法委托,然后调用委托名。
  • 隐式(间接)异步调用:将方法委托,然后使用委托的BeginInvoke方法。
  • 显式(直接)异步调用:使用Thread/Task直接给方法定义线程,然后调用Task名。
  • 单播委托与多播委托:单播委托只委托一个方法,多播委托为一个委托+其他委托。委托调用都是间接调用。
  • 可以使用接口来替代委托,这部分后续再写。

我们可以看一段糟糕的代码:

是不是感觉还好?只是进行了一次套用,那我再拿出如下代码,阁下如何应对?

标签:委托,C#,System,Delagate,int,Student,new,using
From: https://www.cnblogs.com/YiMo9929/p/17725413.html

相关文章

  • abc246F - typewriter
    F-typewriter直接容斥即可,每次选出它们的并集。#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<queue>#definefo(i,a,b)for(int(i)=(a);(i)<=(b);(i)++)#definefd(i,b,a)for(int(i)=(b);(i)>=(a);(i)-......
  • 5.ETC卡 PSAM卡消费流程(转载)
    消费流程打开读卡器0选择psam卡槽1复位psam卡读取psam卡0015文件psamcardsend:00b095000Epsamcardrecv:23010101000000212241010101019000psam卡序列号:23010101000000212241psam卡版本号:01密钥卡类型:01发卡方自定义FCI数据:0101读取psam卡终端机编......
  • ruoyi-cloud免登录访问
    1、首先在nacos中找到gateway.yml编辑,找到ignore下的whites,添加需要名登录的连接2、还要把连接上面的权限校验的代码注解给删除掉......
  • 无涯教程-JavaScript - STANDARDIZE函数
    描述STANDARDIZE函数从以均值和standard_dev为特征的分布返回归一化值。语法STANDARDIZE(x,mean,standard_dev)争论Argument描述Required/OptionalXThevalueyouwanttonormalize.RequiredMeanThearithmeticmeanofthedistribution.RequiredStandard_de......
  • OpenStack(Train版)-部署neutron(二)
    7.2.3、部署自助服务网络Self-servicenetworks7.2.3.1、部署Neutron控制节点(controller)7.2.3.1.1、创库授权createdatabaseneutron;grantallprivilegesonneutron.*to'neutron'@'localhost'identifiedby'neutron123';grantallprivilegesonneut......
  • enclave demo client 运行错误:
    [ec2-user@ip-172-31-66-71GenerateToken]$python3client.pyalias/wallet-sec2-enclave-kmsu001Expectingvalue:line1column1(char0)enclave的console出错信息如下:Startingrun.shnohup:appendingoutputto'nohup.out'[6.471168]NSMRNG:returnin......
  • 关联式数据结构_哈希表剖析 #C++
    哈希概述哈希(hash)又称散列,其基本想法是,将存储的值与其存储位置建立某种映射,因此哈希的查找效率非常高,是一种支持常数平均时间查找的结构。与红黑树相比,哈希的效率表现是以统计为基础的,不需要依赖输入数据的随机性。建立值-址映射建立哈希结构的第一步是将“值”(数据)与“址”(存......
  • Microsoft 365 解决方案:为访客用户创建安全的Microsoft团队和共享频道环境
    博客链接:https://blog.51cto.com/u_13637423随着数字化转型的推进,企业都纷纷采用云端的解决方案来满足日常企业的运作需求,这包含与企业外部的供应商和合作伙伴的业务往来,那么从IT和Secure角度,如何考虑在不影响安全的情况下确保在MicrosoftTeams上与外部用户协作和工作是顺利的呢?·......
  • pytorch(3-0) 可视化训练误差折线图有
     缺点必须手动点击下关闭才能刷新最新的图,起码不会阻塞训练过程     ###画图训练损失训练精度测试精度importmatplotlib.pyplotaspltimportthreadingimporttimeimportmatplotlib.animationasanimationclassAnimator:def__init__(self......
  • DesignWareBuildingBlock IP的仿真与综合
    感谢一下同学的协助,跑通了一个case。IP核调用dw_fp_mac.svmoduledw_fp_mac(inst_a,inst_b,inst_c,inst_rnd,z_inst,status_inst);parameterinst_sig_width=23;parameterinst_exp_width=8;parameterinst_ieee_compliance=0;input[inst_sig_width+inst_exp......