C#委托学习

一、什么是委托

什么是委托 Delegate在这里插入图片描述
委托实例 Action 与 Func
Action 和 Func 是 C# 内置的委托实例,它们都有很多重载以方便使用。
Action用于定义返回类型为void的委托,例:Action<int ,int>表示定义一个传入参数为两个int型变量返回值为void的委托
Func 用于定义有返回值的委托,返回值类型为泛型参数列表中最后一个,例Func <int ,int ,double>表示定义一个传入参数为两个int型变量返回值为double 的类型。

class Program
{
    static void Main(string[] args)
    {
        var calculator = new Calculator();
        // Action 用于无形参无返回值的方法。
        Action action = new Action(calculator.Report); //实例化一个委托类型对象action,指向calculator的report方法
        calculator.Report();
        action.Invoke(); //调用action所指向的方法
        // 模仿函数指针的简略写法。
        action();

        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);

        // Func 也有简略写法。
        z = func1(x, y);
        Console.WriteLine(z);
        z = func2(x, y);
        Console.WriteLine(z);
    }
}

class Calculator
{
    public void Report()
    {
        Console.WriteLine("I have 3 methods.");
    }

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

    public int Sub(int a, int b)
    {
        return a - b;
    }
}

二、委托的声明与调用

在这里插入图片描述
委托是一种类:

static void Main(string[] args)
{
    Type t = typeof(Action);
    Console.WriteLine(t.IsClass);
}

委托是类,所以声明位置是和 class 处于同一个级别。但 C# 允许嵌套声明类(一个类里面可以声明另一个类),所以有时也会有 delegate 在 class 内部声明的情况。
实例:

public delegate double Calc(double x, double y);

class Program
{
    static void Main(string[] args)
    {
        var calculator = new Calculator();
        var calc1 = new Calc(calculator.Mul); //定义一个委托类型对象指向calculator的mul方法

        Console.WriteLine(calc1(5, 6));
    }
}

class Calculator
{
    public double Mul(double x, double y)
    {
        return x * y;
    }

    public double Div(double x, double y)
    {
        return x / y;
    }
}

三、委托的一般使用方法

在这里插入图片描述
模板方法
利用模板方法,提高代码复用性。
下例中 Product、Box、WrapFactory 都不用修改,只需要在 ProductFactory 里面新增不同的 MakeXXX 然后作为委托传入 WrapProduct 就可以对其进行包装。

class Program
{
    static void Main(string[] args)
    {
        var productFactory = new ProductFactory();

        Func<Product> func1 = new Func<Product>(productFactory.MakePizza);
        Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);

        var wrapFactory = new WrapFactory();
        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)
    {
        var box = new Box();
        Product product = getProduct.Invoke();
        box.Product = product;
        return box;
    }
}

class ProductFactory
{
    public Product MakePizza()
    {
        var product = new Product();
        product.Name = "Pizza";
        return product;
    }

    public Product MakeToyCar()
    {
        var product = new Product();
        product.Name = "Toy Car";
        return product;
    }
}

Reuse,重复使用,也叫“复用”。代码的复用不但可以提高工作效率,还可以减少 bug 的引入。
良好的复用结构是所有优秀软件所追求的共同目标之一。
回调方法
回调方法是通过委托类型参数传入主调方法的被调用方法,主调方法根据自己的逻辑决定是否调用这个方法。

class Program
{
    static void Main(string[] args)
    {
        var productFactory = new ProductFactory();
        
        // Func 前面是传入参数,最后一个是返回值,所以此处以 Product 为返回值
        Func<Product> func1 = new Func<Product>(productFactory.MakePizza);
        Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);

        var wrapFactory = new WrapFactory();
        var logger = new Logger();
        // Action 只有传入参数,所以此处以 Product 为参数
        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)
    {
        // Now 是带时区的时间,存储到数据库应该用不带时区的时间 UtcNow。
        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)
    {
        var box = new Box();
        Product product = getProduct.Invoke();

        // 只 log 价格高于 50 的
        if (product.Price >= 50)
        {
            logCallBack(product);
        }

        box.Product = product;
        return box;
    }
}

class ProductFactory
{
    public Product MakePizza()
    {
        var product = new Product
        {
            Name = "Pizza",
            Price = 12
        };
        return product;
    }

    public Product MakeToyCar()
    {
        var product = new Product
        {
            Name = "Toy Car",
            Price = 100
        };
        return product;
    }
}

四、委托的高级使用

在这里插入图片描述
多播(multicast)委托
多播委托即一个委托内部封装不止一个方法。(一个委托中添加了另一个委托,顺序执行)

using System;
using System.Threading;

namespace DelegateExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var stu1 = new Student { ID = 1, PenColor = ConsoleColor.Yellow };
            var stu2 = new Student { ID = 2, PenColor = ConsoleColor.Green };
            var stu3 = new Student { ID = 3, PenColor = ConsoleColor.Red };
            var action1 = new Action(stu1.DoHomework);
            var action2 = new Action(stu2.DoHomework);
            var action3 = new Action(stu3.DoHomework);

            // 单播委托
            //action1.Invoke();
            //action2.Invoke();
            //action3.Invoke();

            // 多播委托
            action1 += action2;
            action1 += action3;

            action1.Invoke();
        }
    }

    class Student
    {
        public int ID { get; set; }
        public ConsoleColor PenColor { get; set; }

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

隐式异步调用在这里插入图片描述
异步互不相干:
这里说的“互不相干”指的是逻辑上,而现实工作当中经常会遇到多个线程共享(即同时访问)同一个资源(比如某个变量)的情况,这时候如果处理不当就会产生线程间争夺资源的冲突。

三种同步调用

using System;
using System.Threading;

namespace DelegateExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var stu1 = new Student { ID = 1, PenColor = ConsoleColor.Yellow };
            var stu2 = new Student { ID = 2, PenColor = ConsoleColor.Green };
            var stu3 = new Student { ID = 3, PenColor = ConsoleColor.Red };

            // 直接同步调用
            //stu1.DoHomework();
            //stu2.DoHomework();
            //stu3.DoHomework();

            var action1 = new Action(stu1.DoHomework);
            var action2 = new Action(stu2.DoHomework);
            var action3 = new Action(stu3.DoHomework);

            // 间接同步调用
            //action1.Invoke();
            //action2.Invoke();
            //action3.Invoke();

            // 多播委托,同步调用
            action1 += action2;
            action1 += action3;

            action1.Invoke();

            // 主线程模拟在做某些事情。
            for (var 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 = 0; i < 5; i++)
            {
                Console.ForegroundColor = PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s)", ID, i);
                Thread.Sleep(1000);
            }
        }
    }
}

三种同步调用的结果一样:
在这里插入图片描述
使用委托进行隐式异步调用 BeginInvoke

using System;
using System.Threading;

namespace DelegateExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var stu1 = new Student { ID = 1, PenColor = ConsoleColor.Yellow };
            var stu2 = new Student { ID = 2, PenColor = ConsoleColor.Green };
            var stu3 = new Student { ID = 3, PenColor = ConsoleColor.Red };

            var action1 = new Action(stu1.DoHomework);
            var action2 = new Action(stu2.DoHomework);
            var action3 = new Action(stu3.DoHomework);

            // 使用委托进行隐式异步调用。
            // BeginInvoke 自动生成分支线程,并在分支线程内调用方法。
            action1.BeginInvoke(null, null);
            action2.BeginInvoke(null, null);
            action3.BeginInvoke(null, null);

            // 主线程模拟在做某些事情。
            for (var 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 = 0; i < 5; i++)
            {
                Console.ForegroundColor = PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s)", ID, i);
                Thread.Sleep(1000);
            }
        }
    }
}

运行结果
在这里插入图片描述
使用 Thread 与 Task 进行异步调用

using System;
using System.Threading;
using System.Threading.Tasks;

namespace DelegateExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var stu1 = new Student { ID = 1, PenColor = ConsoleColor.Yellow };
            var stu2 = new Student { ID = 2, PenColor = ConsoleColor.Green };
            var stu3 = new Student { ID = 3, PenColor = ConsoleColor.Red };

            // 老的显式异步调用方式 Thread
            //var thread1 = new Thread(new ThreadStart(stu1.DoHomework));
            //var thread2 = new Thread(new ThreadStart(stu2.DoHomework));
            //var thread3 = new Thread(new ThreadStart(stu3.DoHomework));

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

            // 使用 Task
            var task1 = new Task(new Action(stu1.DoHomework));
            var task2 = new Task(new Action(stu2.DoHomework));
            var task3 = new Task(new Action(stu3.DoHomework));

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

            // 主线程模拟在做某些事情。
            for (var 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 = 0; i < 5; i++)
            {
                Console.ForegroundColor = PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s)", ID, i);
                Thread.Sleep(1000);
            }
        }
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值