C#委托详解

什么是委托(delegate)

委托就是函数指针升级版

函数指针

指针的概念这里就不再赘述了,学过C语言的应该都知道。
在C语言中函数指针的定义:

//Calc可以更换为其他的字符串
int(*Calc)(int, int);

int Method0(int a , int b) {
	return 0;
}

int Method1(int a , int b) {
	return 1;
}
//调用
int main(){
	Calc funcPoint0 = &Method0;
	Calc funcPoin10 = &Method1;
}

就是说这种类型的的数据,指向一个参数为(int , int),返回地址为int的方法地址,不在乎该方法体内部是如何实现的。
点这里了解更多

C#中常用委托–Action和Func及自定义委托实现

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

namespace Calculator
{
    //自定义委托 返回值为double  参数为double类型的两参数
    public delegate double Calc(double x, double y);
    internal class Program
    {
        static void Main(string[] args)
        {
            Calculator co = new Calculator();
            Action ac = new Action(co.Report);//无参委托的声明 需要无参方法并且无返回值
            Console.WriteLine("由C#提供的Action委托:");
            Console.WriteLine("直接调用:"); 
            co.Report();//通过类实例直接调用方法
            Console.WriteLine("间接调用:");
            ac.Invoke();//通过委托间接调用实例方法1
            ac();//通过委托间接调用实例方法2

            //第二种形式委托 泛型委托 最后一个为方法的返回类型,前面的为参数
            Func<int, int, int> funcAdd = new Func<int, int, int>(co.Add);
            Func<int, int, int> funcSub = new Func<int, int, int>(co.Sub);

            int a = 100;
            int b = 200;
            int result = 0;

            result = funcAdd(a, b);//调用
            Console.WriteLine("C#提供的泛型委托调用加法: " + result);

            result = funcSub(a, b);//调用
            Console.WriteLine("C#提供的泛型委托调用减法: " + result);
            //cw + 两次Tab = Console.WriteLine();


            //自定义委托调用
            double x = 100;
            double y = 100;
            Calc calc = new Calc(co.Add);
            Console.WriteLine("自定义委托以Invoke()调用double加法结果:" + calc.Invoke(x , y));//使用Invoke方法调用
            Console.WriteLine("自定义委托以函数指针方式调用double加法结果:" + calc(x , y));//使用函数指针方式调用

            Console.ReadLine();
        }
    }

    class Calculator {
        public void Report()
        {
            Console.WriteLine("Calculator 中的report方法。");
        }
        //使用C#提供的委托调用
        public int Add(int a , int b)
        {
            return a + b;
        }
        //使用C#提供的委托调用的方法
        public int Sub(int a, int b)
        {
            return a - b;
        }

        //使用自定义委托调用
        public double Add(double a, double b)
        {
            return a + b;
        }
    }

}

如何正确使用委托

如何正确使用委托,让一个方法调用另一个方法呢?或者说两个方法满足什么条件调用时使用委托比较呢?
1、模板方法
某个方法中有一个操作需要根据情况选择调用某些方法中的一个(根据参数的类型不同区分),则可使用委托。
特点:位于方法中部,是必须执行的某个操作且有返回值。

2、回调方法
某个方法中有一个操作需要根据情况选择调用某些方法中的一个或者不调用,则也可使用委托。
特点:通常位于方法尾部,并不是必须执行的,可以执行也可以不执行,且无返回值。

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

namespace Delegant3
{
    internal class Program
    {
        static void Main(string[] args)
        {
            WrapFactory wf = new WrapFactory();//创建包装工厂
            ProductFactory pf = new ProductFactory();//创建产品工厂
            //定义泛型委托一个生产蛋糕 一个生产飞机 这里的Product为调用方法的返回值
            Func<Product> fucForCake = new Func<Product>(pf.MakeCake);
            Func<Product> fucForAirplan = new Func<Product>(pf.MakeAirplane);
            //定义Action委托 <xxx>中的xxx是方法参数类型
            Logger logger = new Logger();
            Action<Product> log = new Action<Product>(logger.Log);

            Box box1 = wf.WrapProduct(fucForCake , log);
            Box box2 = wf.WrapProduct(fucForAirplan, log);


            Console.WriteLine(box1.Product.Name);
            Console.WriteLine(box2.Product.Name);

            Console.ReadLine();
        }
    }


    class Logger
    {
        public void Log(Product product)
        {
            //UTCNow不带时区,Now带时区
            Console.WriteLine("产品{0},生产于{1},价格为:{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 WrapFactory
    {
        //参数为委托 该委托返回的值是Product
        public Box WrapProduct(Func<Product> getProduct , Action<Product> loggerCallback)
        {
            Box box = new Box();
            //通过委托调用 必须执行生产产品 位于方法中部 且有返回值--Box 模板方法
            Product product = getProduct();
            box.Product = product;

            //当价格小于五百时不执行,大于等于五百时执行 并且位于尾部 无返回值
            if(product.Price >= 500)
            {
                loggerCallback(product);
            }
            return box;
        }
    }

    class ProductFactory
    {
        public Product MakeCake()
        {
            //在这里创建产品蛋糕
            Product product = new Product();
            product.Name = "蛋糕";
            product.Price = 50;
            return product;
        }

        public Product MakeAirplane()
        {
            //在这里创建产品飞机
            Product product = new Product();
            product.Name = "飞机";
            product.Price = 5000;
            return product;
        }
    }
}

委托的局限

方法级别的紧耦合。
会降低代码的可读性,并且Debug难度更大。
会让代码更加地难以维护。
使用不当会导致内存泄露或者程序性能下降。

其他的委托

多播委托

内部封装了多个方法一个委托,称之为多播委托

隐式异步调用

使用多线程,让多个委托之间并发执行。

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

namespace MulitcastDelegate
{
    class Program
    {
        static void Main(string[] args)
        {
            /* Student stu1 = new Student(1 , ConsoleColor.Red);*/
            Student stu1 = new Student() { Id = 1, Color = ConsoleColor.Red };
            Student stu2 = new Student(){ Id = 2 , Color = ConsoleColor.Yellow};
            Student stu3 = new Student() { Id = 3, Color = ConsoleColor.Green };

            //同步直接调用
           /* stu1.DoHomework();
            stu2.DoHomework();
            stu3.DoHomework();*/
            
            Action action1 = new Action(stu1.DoHomework);
            Action action2 = new Action(stu2.DoHomework);
            Action action3 = new Action(stu3.DoHomework);
            //多播--同步间接调用 按顺序执行
            /*action1 += action2;
            action1 += action3;*/
            /*action1.Invoke();*/

            /* action1.BeginInvoke(null , null);//单播--隐式异步间接调用 随机执行
             action2.BeginInvoke(null, null);
             action3.BeginInvoke(null, null);*/

            //显式异步调用1 - Thread
            /*Thread th1 = new Thread(new ThreadStart(stu1.DoHomework));
            Thread th2 = new Thread(new ThreadStart(stu2.DoHomework));
            Thread th3 = new Thread(new ThreadStart(stu3.DoHomework));
            th1.Start();
            th2.Start();
            th3.Start();*/

            //显式异步调用2 - Task
            Task t1 = new Task(action1);
            Task t2 = new Task(action2);
            Task t3 = new Task(action3);

            t1.Start();
            t2.Start();
            t3.Start();

            for (int i = 0; i < 10; i++)
            {
                Console.ForegroundColor = ConsoleColor.Blue;
                Console.WriteLine("Main {0} ", i);
                Thread.Sleep(1000);
            }

            Console.ReadLine();
        }
    }

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

      /*  public Student(int id , ConsoleColor color)
        {
            this.Id = id;
            this.Color = color;
        }*/

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

}

在Java中完全使用接口代替委托,我就说委托看着很眼熟。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值