首页 > 编程语言 > 详细

C#语言入门(11)委托详解

时间:2021-08-03 18:37:52      阅读:15      评论:0      收藏:0      [点我收藏+]

1、什么是委托

(1)委托( delegate )是函数指针的“升级版”

  • 实例: C/C++中的函数指针

    (2)一切皆地址

  • 变量(数据)是以某个地址为起点的一段内存中所存储的值
  • 函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令

    (3)直接调用与间接调用

  • 直接调用:通过函数名来调用函数,CPU通过函数名直接获得函数所在地址并开始执行->返回
  • 间接调用:通过函数指针来调用函数,CPU通过读取函数指针存储的值获得函数所在地址并开始
    执行>返回

    (4)Java中没有与委托相对应的功能实体

    (5)委托的简单使用

  • Action委托
  • Func委托
    ```c#
    namespace ConsoleApp2
    {
    class Program
    {
    static void Main(string[] args)
    {
    Calculator calculator = new Calculator();
    Action action = new Action(calculator.Report);
    calculator.Report();
    action.Invoke();
    Func<int, int, int> func1 = new Func<int, int, int>(calculator.Add);
    Func<int, int, int> func2 = new Func<int, int, int>(calculator.Sub);
    int x = 100;
    int y = 200;
    int z = 0;
    z = func1.Invoke(x, y);
    Console.WriteLine(z);
    z = func2.Invoke(x, y);
    Console.WriteLine(z);
    }
    }

    class Calculator
    {
    public void Report()
    {
    Console.WriteLine("I have 3 method:");
    }
    public int Add(int a,int b)
    {
    int result = a + b;
    return result;
    }
    public int Sub(int a, int b)
    {
    int result = a - b;
    return result;
    }
    }

}

## 2、委托的声明(自定义委托)
### (1)委托是一种类( class ),类是数据类型所以委托也是一种数据类型

### (2)它的声名方式与般的类不同 ,主要是为 了照顾可读性和C/C+ +传统
### (3)注意声明委托的位置
- 避免写错地方结果声明成嵌套类型
### (4)委托与所封装的方法必需“类型兼容”
![image.png](https://s2.51cto.com/images/20210802/1627893583823783.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
```c#
namespace ConsoleApp2
{
    public delegate double Calc(double x, double y);
    class Program
    {
        static void Main(string[] args)
        {
            Calculator calculator = new Calculator();
            Calc calc1 = new Calc(calculator.Add);
            Calc calc2 = new Calc(calculator.Sub);
            Calc calc3 = new Calc(calculator.Mul);
            Calc calc4 = new Calc(calculator.Div);
            double x = 100;
            double y = 200;
            double z = 0;
            z = calc1(x, y);
            Console.WriteLine(z);
            z = calc2(x, y);
            Console.WriteLine(z);
            z = calc3(x, y);
            Console.WriteLine(z);
            z = calc4(x, y);
            Console.WriteLine(z);
        }            
    }  

    class Calculator
    {
        public  double Add(double a, double b)
        {
           return a + b;
        }
        public  double Sub(double a, double b)
        {
           return a - b;
        }
        public  double Mul(double a, double b)
        {
           return a * b;
        }
        public  double Div(double a, double b)
        {            
           return a / b;
        }

    }

}

3、委托的一般使用

(1)实例:把方法当作参数传给另一个方法

  • 正确使用1 :模板方法,“借用”指定的外部方法来产生结果
    • 相当于“填空题"
    • 常位于代码中部
    • 委托有返回值
  • 正确使用2 :回调( callback )方法,调用指定的外部方法
    • 相当于"流水线”
    • 常位于代码末尾
    • 委托无返回值

      (2)注意:难精通+易使用+功能强大东西,一旦被滥用则后果非常严重

  • 缺点1:这是一种方法级别的紧耦合,现实工作中要慎之又慎
  • 缺点2 :使可读性下降、debug的难度增加
  • 缺点3 :把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
  • 缺点4 :委托使用不当有可能造成内存泄漏和程序性能下降

模板方法
```c#
namespace ConsoleApp2
{
public delegate double Calc(double x, double y);
class Program
{
static void Main(string[] args)
{
ProductFactory productFactory = new ProductFactory();
WrapFactory wrapFactory = new WrapFactory();
Func<Product> func1 = new Func<Product>(productFactory.MakePizza);
Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);
Box box1 = wrapFactory.WrapProduct(func1);
Box box2 = wrapFactory.WrapProduct(func2);
Console.WriteLine(box1.Product.Name);
Console.WriteLine(box2.Product.Name);
}
}

class Product
{
   public string Name { get; set; }
}
class Box
{
    public Product Product { get; set; }
}
class WrapFactory
{
    public Box WrapProduct(Func<Product>getProduct)
    {
        Box box = new Box();
        Product product = getProduct.Invoke();
        box.Product = product;
        return box;
    }
}
class ProductFactory
{
    public Product MakePizza()
    {
        Product product = new Product();
        product.Name = "Pizza";
        return product;
    }
    public Product MakeToyCar()
    {
        Product product = new Product();
        product.Name = "Toy Car";
        return product;
    }
}

}

回调方法

```c#
namespace ConsoleApp2
{
    public delegate double Calc(double x, double y);
    class Program
    {
        static void Main(string[] args)
        {
            ProductFactory productFactory = new ProductFactory();
            WrapFactory wrapFactory = new WrapFactory();
            Func<Product> func1 = new Func<Product>(productFactory.MakePizza);
            Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);
            Logger logger = new Logger();
            Action<Product> log = new Action<Product>(logger.Log);
            Box box1 = wrapFactory.WrapProduct(func1,log);
            Box box2 = wrapFactory.WrapProduct(func2,log);
            Console.WriteLine(box1.Product.Name);
            Console.WriteLine(box2.Product.Name);
        }
    }
    class Logger
    {
        public void Log(Product product)
        {
            Console.WriteLine("Product ‘{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 WrapFactory
    {
        public Box WrapProduct(Func<Product>getProduct,Action<Product>logCallback)
        {
            Box box = new Box();
            Product product = getProduct.Invoke();
            if (product.Price>=50)
            {
                logCallback(product);
            }
            box.Product = product;
            return box;
        }
    }
    class ProductFactory
    {
        public Product MakePizza()
        {
            Product product = new Product();
            product.Name = "Pizza";
            product.Price = 12;
            return product;
        }
        public Product MakeToyCar()
        {
            Product product = new Product();
            product.Name = "Toy Car";
            product.Price = 100;
            return product;
        }
    }
}

4、委托的高级使用

(1)多播( multicast)委托

```c#
namespace ConsoleApp2
{
public delegate double Calc(double x, double y);
class Program
{
static void Main(string[] args)
{
Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };
Action action1 = new Action(stu1.DoHomework);
Action action2 = new Action(stu2.DoHomework);
Action action3 = new Action(stu3.DoHomework);
action1 += action2;
action1 += action3;
action1.Invoke();

    }
}

class Student
{
   public int ID { get; set; }
    public ConsoleColor PenColor { get; set; }
    public void DoHomework()
    {
        for (int i = 1; i < 5; i++)
        {
            Console.ForegroundColor = this.PenColor;
            Console.WriteLine("Student{0} doing homework{1} hours(s).",this.ID,i);
            Thread.Sleep(1000);
        }
    }
}

}


### (2)隐式异步调用
- 同步与异步的简介
  - 中英文的语言差异
  - 同步:你做完了我(在你的基础上)接着做
  - 异步:咱们两个同时做(相当于汉语中的“同步进行")
### (3)同步调用与异步调用的对比
- 每一个运行的程序是一个进程 ( process )
- 每个进程可以有一一个或者多个线程( thread )
- 同步调用是在同一线程内
- 异步调用的底层机理是多线程
- 串行=同步=单线程, 并行=异步=多线程

### (4)隐式多线程 V.S 显式多线程
- 直接同步调用:使用方法名
```c#
namespace ConsoleApp2
{
    public delegate double Calc(double x, double y);
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };
            stu1.DoHomework();
            stu2.DoHomework();
            stu3.DoHomework();
            for (int i = 0; i < 10; i++)
            {
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("Main thread{0}",i);
                Thread.Sleep(1000);
            }

        }
    }

    class Student
    {
       public int ID { get; set; }
        public ConsoleColor PenColor { get; set; }
        public void DoHomework()
        {
            for (int i = 1; i < 5; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student{0} doing homework{1} hours(s).",this.ID,i);
                Thread.Sleep(1000);
            }
        }
    }
}
  • 间接同步调用:使用单播/多播委托的Invoke方法
    ```c#
    namespace ConsoleApp2
    {
    public delegate double Calc(double x, double y);
    class Program
    {
    static void Main(string[] args)
    {
    Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
    Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
    Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };
    Action action1 = new Action(stu1.DoHomework);
    Action action2 = new Action(stu2.DoHomework);
    Action action3 = new Action(stu3.DoHomework);
    action1.Invoke();
    action2.Invoke();
    action3.Invoke();
    }
    }

    class Student
    {
    public int ID { get; set; }
    public ConsoleColor PenColor { get; set; }
    public void DoHomework()
    {
    for (int i = 1; i < 5; i++)
    {
    Console.ForegroundColor = this.PenColor;
    Console.WriteLine("Student{0} doing homework{1} hours(s).",this.ID,i);
    Thread.Sleep(1000);
    }
    }
    }
    }

  • 隐式异步调用:使用委托的Beginlnvoke
    ```c#
    namespace ConsoleApp2
    {
    public delegate double Calc(double x, double y);
    class Program
    {
    static void Main(string[] args)
    {
    Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
    Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
    Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };
    Action action1 = new Action(stu1.DoHomework);
    Action action2 = new Action(stu2.DoHomework);
    Action action3 = new Action(stu3.DoHomework);
    action1.BeginInvoke(null, null) ;
    action2.BeginInvoke(null, null) ;
    action3.BeginInvoke(null, null) ;

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

    }

    class Student
    {
    public int ID { get; set; }
    public ConsoleColor PenColor { get; set; }
    public void DoHomework()
    {
    for (int i = 1; i < 5; i++)
    {
    Console.ForegroundColor = this.PenColor;
    Console.WriteLine("Student{0} doing homework{1} hours(s).",this.ID,i);
    Thread.Sleep(1000);
    }
    }
    }
    }

  • 显式异步调用:使用Thread或Task
    ```c#
    namespace ConsoleApp2
    {
    public delegate double Calc(double x, double y);
    class Program
    {
    static void Main(string[] args)
    {
    Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
    Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
    Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };
    Task task1 = new Task(new Action(stu1.DoHomework));
    Task task2 = new Task(new Action(stu2.DoHomework));
    Task task3 = new Task(new Action(stu3.DoHomework));
    task1.Start();
    task2.Start();
    task3.Start();

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

    }

    class Student
    {
    public int ID { get; set; }
    public ConsoleColor PenColor { get; set; }
    public void DoHomework()
    {
    for (int i = 1; i < 5; i++)
    {
    Console.ForegroundColor = this.PenColor;
    Console.WriteLine("Student{0} doing homework{1} hours(s).",this.ID,i);
    Thread.Sleep(1000);
    }
    }
    }

(5)应该适时地使用接口( interface )取代一些对委托的使用

  • Java完全地使用接口取代了委托的功能,即Java没有与C#中委托相对应的功能实体
    ```c#
    namespace ConsoleApp2
    {
    public delegate double Calc(double x, double y);
    class Program
    {
    static void Main(string[] args)
    {
    IProductFactory pizzaFactory = new PizzaFactory();
    IProductFactory toycarFactory = new ToyCarFactory();
    WrapFactory wrapFactory = new WrapFactory();
    Box box1 = wrapFactory.WrapProduct(pizzaFactory);
    Box box2 = wrapFactory.WrapProduct(toycarFactory);
    Console.WriteLine(box1.Product.Name);
    Console.WriteLine(box2.Product.Name);
    }
    }
    interface IProductFactory
    {
    Product Make();
    }
    class PizzaFactory : IProductFactory
    {
    public Product Make()
    {
    Product product = new Product();
    product.Name = "Pizza";
    return product;
    }
    }
    class ToyCarFactory : IProductFactory
    {
    public Product Make()
    {
    Product product = new Product();
    product.Name = "Toy Car";
    return product;
    }
    }

    class Product
    {
    public string Name { get; set; }
    }
    class Box
    {
    public Product Product { get; set; }
    }
    class WrapFactory
    {
    public Box WrapProduct(IProductFactory productFactory)
    {
    Box box = new Box();
    Product product = productFactory.Make();
    box.Product = product;
    return box;
    }
    }
    }

C#语言入门(11)委托详解

原文:https://blog.51cto.com/u_15296868/3258564

(0)
(0)
   
举报
评论 一句话评论(0
分享档案
最新文章
教程昨日排行
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!