委托-C#

包装成变量

我们先来看委托的用法

using System;

namespace CSharplearn
{
    class Program
    {
        public delegate void Remainder(int num);//声明委托
        static void Main(string[] args)
        {
            Remainder remainder = new Remainder(MethodRemainder/*传给委托的方法*/);//实例化委托
            remainder(10);//调用委托里的方法
        }
        public static void MethodRemainder(int num) //声明方法
        {
            if (num % 2 == 0) //方法主体
            {
                Console.WriteLine("这个整数取余数是0");
            }
        }
    }
}

你要用委托,肯定要先声明委托,声明委托的方式是

作用域修饰符 delegate 返回值类型 委托名(参数);

声明一个委托和声明一个方法差不多,区别在于多了一个delegate,以及不需要方法主体.
声明委托可以在任何地方,除了方法内,当然,注意作用域.
调用委托必须实例化委托,也就是new一个委托.委托需要传一个方法给变量,现在我们来写这个方法.
如果这个方法要传给委托,那么他的返回值和参数要和委托一样.
然后我们写一个MethodRemainder方法,他会判断传进来到参数取余数是不是等于0如果是就打印"这个整数取余数是0".
现在在new Remainder()的()里面填上MethodRemainder,不要在方法后面加(),因为是传方法,而不是调用这个方法.
写完Remainder remainder = new Remainder(MethodRemainder)后,你就可以通过remainder(参数)来调用里面的方法.
结果:
在这里插入图片描述
然后我们就知道了委托的第一个作用,可以把方法包装成变量(在这里就是Remainder类的变量remainder)
于是你就会产生所有初学委托的人都会有的疑问,为什么调用一个方法要这么费劲,我直接调用方法名他不好吗?
要解释这个疑问,还需要看下一个例子

解耦与封闭

我们先把委托去掉,然后把取余数方法移到另一个文件去
文件一:Program.cs

namespace CSharplearn
{
    class Program
    {
        static void Main(string[] args)
        {
            RemainderClass.MethodRemainder(10);
        }
    }
}

文件二:RemainderClass.cs

using System;
class RemainderClass
{
    public static void MethodRemainder(int num)
    {
        if (num % 2 == 0)
        {
            Console.WriteLine("这个整数取余数是0");
        }
    }
}

运行结果和上面一样就不展示了
现在我们需要多几种方式来取余数,那就在RemainderClass类里多写那几种方法.

using System;
class RemainderClass
{
    public static void MethodRemainder(int num)
    { 
        if (num % 2 == 0)
        {
            Console.WriteLine("这个整数取余数是0");
        }
    }
    public static void MethodRemainderTwo(decimal num) 
    {
        if (Math.Truncate(num) % 2 == 0) 
        {
            Console.WriteLine("这个小数的整数部分取余数是0");
        }
    }
    public static void MethodRemainderThree(int num) 
    {
        if (Math.Pow(num, 3) % 2 == 0) 
        {
            Console.WriteLine("这个整数^3之后取余数是0");
        }
    }
}

我又新加了第二种和第三种方法.第二种方法会把参数先取整数部分然后再除以2,Math.Truncate方法的作用是把传进来的decimal参数舍去小数部分然后取整数.第三种方法会把参数先进行三次幂然后在除以2.Math.Pow的作用是返回第一个参数的第二个参数次幂.
至此,判断用户输入的数字除以2余数是否为0这个小程序暂时告一段落.
好,若是未来我们要继续添加新的判断方法直接在RemainderClass.cs上下刀子就可以了.
但假如,我们有两个人来维护这个程序呢?现在你要把自己精分成两个人.一个人小A维护Program.cs,另一个人小B维护RemainderClass.cs.如果小A有了新的数据类型,例如double类,或者某个其他类里的数据需要进行取余数判断,他就要通知小B来添加新的方法来支持他的需求.小A他主要负责Program.cs,而且因为各种各样的原因不能去维护RemainderClass.cs,所以每次他有新的需求,他就必须去找小B,这样麻不麻烦?顺带一提,两者之间有互相依赖的关系,这个就称之为耦合
所以合理的方式是,每当小A有需求,他应该自己来编写逻辑来满足他的需求.小B只需要负责主要功能的实现.现在来审视代码,MethodRemainder的主要功能应该是接受数据和逻辑,输出结果.而小A需要编写逻辑,例如我这个数据究竟该怎么判断.
那么小A和小B的方法,把两者联系起来的关键是什么?答案就是委托.
现在来看加入委托之后代码变成了什么样子.
文件一:Program.cs

using System;

namespace CSharplearn
{
    class Program
    {
        public delegate bool Remainder(int num);
        static void Main(string[] args)
        {
            Remainder remainder = new Remainder(RemainderLogic);
            RemainderClass.MethodRemainder(12, remainder);
        }
        public static bool RemainderLogic(int num) 
        {
            return num % 2 == 0;
        }
        public static bool RemainderLogicThree(int num) 
        {
            return Math.Pow(num, 3) % 2 == 0;
        }
    }
}

文件2:RemainderClass.cs

using System;

class RemainderClass
{
    public static void MethodRemainder(int num,CSharplearn.Program.Remainder remainder) 
    {
        if (remainder(num))
        {
            Console.WriteLine($"{num}取余数是0");
        }
        else
        {
            Console.WriteLine($"{num}取余数不是0");
        }
    }
}

(我少写了一个逻辑,后面会讲)
(CSharplearn.Program.Remainder可以用using static CSharplearn.Program简化成 Remainder,因为本文重点不在using上,怕新人读了会混乱,故采用这种方式)
我来一步步讲解这个程序是如何运行的.
首先定义了一个委托Remainder,返回值是bool,参数是int类型的.Main()方法里实例化了一个委托,传的方法是RemainderLogic.现在我们来看RemainderLogic,参数模2等于0的话就返回true.回到Main(),第二条语句调用了RemainderClass里的方法MethodRemainder.现在跳到Remainder类里来看这个MethodRemainder方法.他的两个参数一个是数据(int num),一个是逻辑(CSharplearn.Program.Remainder remainder).因为remainder里面已经有一个方法RemainderLogic了,所以执行remainder(),就相当于执行RemainderLogic().所以.if(remainder(num))现在就等价与if(参数%2==0).
调用MethodRemainder()的时候,传了一个要判断的数据12和已经包好逻辑的remainder.现在来看结果:
在这里插入图片描述
如果我把new Remainder(RemainderLogic)的RemainderLogic替换成RemainderLogicThree,MethodRemainder判断的逻辑就变成了RemainderLogicThree.
这样做,RemainderClass就封闭起来了,而且两个类关系从互相依赖到依赖委托.当两者关系中间出现了中间商的时候,就称之为解耦

泛型委托

细心的同学发现了,我少写了一个参数为decimal的逻辑.这是因为这个逻辑的参数与委托的参数不一致,导致该委托无法容下这个逻辑.那怎么办,难道要为了参数类型不同就要多出n倍工作量来支持不同类型的参数?
不用,委托也支持泛型.泛型简单的来说就是把类型也化作一个可以变化的东西,不用一开始就声明,只有在我实际声明他是什么类型的时候他才变成什么类型.
下面来看加入了泛型委托后的代码
文件一:Program.cs

using System;

namespace CSharplearn
{
    class Program
    {
        public delegate bool Remainder<T>(T num);
        static void Main(string[] args)
        {
            Remainder<decimal> remainder = new Remainder<decimal>(RemainderLogicTwo);
            RemainderClass.MethodRemainder<decimal>(12.9M, remainder);
        }
        public static bool RemainderLogic(int num) 
        {
            return num % 2 == 0;
        }
        public static bool RemainderLogicTwo(decimal num) 
        {
            return Math.Truncate(num) % 2 == 0;
        }
        public static bool RemainderLogicThree(int num) 
        {
            return Math.Pow(num, 3) % 2 == 0;
        }
    }
}

文件2:RemainderClass.cs

using System;
class RemainderClass
{
    public static void MethodRemainder<T>(T num,CSharplearn.Program.Remainder<T> remainder) 
    {
        if (remainder(num))
        {
            Console.WriteLine($"{num}取余数是0");
        }
        else
        {
            Console.WriteLine($"{num}取余数不是0");
        }
    }
}

声明泛型委托的格式是
作用域修饰符 delegate 返回值类型 委托名< T >(T 参数);
这个T可以变化成任何名称,不过在写泛型的时候大家习惯写成T.
所以该怎么用呢?首先我补上了缺失的那个逻辑.在实例化委托的时候把这个逻辑传进去.因为这个逻辑用的参数是decimal,所以<>的内容也要变成decimal.当然处理数据的核心方法也要升级为泛型方法,使用泛型方法和泛型委托差不多.
如果要把逻辑换成RemainderLogic,改类型只需要往<>里填int就行了.
结果:
在这里插入图片描述
由于泛型不是本文重点,所以讲的不是很详细.只是顺带说明委托也可以使用泛型.

委托的多播

多播委托,其实就是一个委托承载多个方法.
由于我的上面的例子不好讲多播委托,我们来看一个新例子

using System;

namespace CSharplearn
{
    class Program
    {
        public delegate int Calculator(int a,int b);
        static void Main(string[] args)
        {
            Calculator calculator = new Calculator(Sum);
            calculator += new Calculator(Mul);
            calculator(2, 5);
            Console.WriteLine($"存储的数字大小是{ Save.NumberSaver}");
        }
        public static int Sum(int a, int b) 
        {
            return Save.NumberSaver += a + b;
        }
        public static int Mul(int a, int b) 
        {
            return Save.NumberSaver += a * b;
        }
    }

    static class Save 
    {
        public static int NumberSaver;
    }
}

这不是最简单的案例,但是能说明多播委托的大部分事情.
首先在初次实例化的时候传了Sum这个方法不难理解.
但是在第二就有了+=,把这个式子展开来就是calculator = calculator + new Calculator(Mul),意思就是说在原有的基础上再往里面赛一个方法Mul.
最后调用这个委托,传递参数2和5.多播委托的执行顺序是第一个塞的方法先执行,于是2+5=7,7这个数值被塞进了Save.NumberSaver;后塞的方法后执行,2*5=10,10这个数值也被塞进了Save.NumberSaver.于是Save.NumberSaver应该有7+10=17
来看结果正不正确:
在这里插入图片描述
如果后来我想移除calculator里的Mul方法,那就calculator -= new Calculator(Mul);,-=是用来去除的.
有些博客写着多播委托返回值必须为void云云,否则就算有返回值也不知道返回到哪里,那么这个案例就完美的打了他们的脸.返回值需要有个地方储存,在这份案例里就是Save类里的NumberSaver.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值