C#编程:~你所了解的C#委托

前言:

我是一个C++的铁杆粉丝,但是现在还是一个正在学习C++ 的小白。因为毕业后想搞游戏开发,所用的引擎是Unity,所以不得不好好学习C#。我在从C++ 转而学习C#时,遇到的第一个问题就是C# 中的委托和事件。(我认为C# 的委托特别像C++ 中的函数指针,但是比函数指针要强大)。本篇博文我将会详细讲解什么是C# 的委托,以及关于C# 委托的相关常用操作方法。


C#委托的引入:

在中国的传统教育中,考试成绩被认为是可以判断一个学生的优秀程度。现在初中生小明回到家里,家里人问他数学考了多少(可怜的小明刚好及格),于是小明告诉了家里数学考了多少分:

        public static void SayMyScores(int number)
        {
            MathScore(number);
        }
        public static void MathScore(int number)
        {
            Console.WriteLine("Math:" + number);
        }
        static void Main(string[] args)
        {
            Program.SayMyScores(60);
        }

然后家里人问他语文考了多少,那么针对上个程序的问题来了:

我们能做的是再写一个函数,然后修改SayScores函数:(不幸运的是小明的语文也是60分)

       public static void SayMyScores(int number)
        {
            LanguageScore(number);
        }
        public static void MathScore(int number)
        {
            Console.WriteLine("Math:" + number);
        }
        public static void LanguageScore(int number)
        {
            Console.WriteLine("Language:" + number);
        }
        static void Main(string[] args)
        {
            Program.SayMyScores(60);
        }


现在你也能够看出来了,这种方式的扩展性非常低。而且对于这种类似的需求,几乎是从头修改到尾。那么这种时候我们必须对此结构进行修改.或许你想的是使用 if ~else或者是 switch 来优化:

       enum Sbuject
        {
            Langguage,
            Math
        }
        public static void SayMyScores(int number,Sbuject sbu)
        {
            if (sbu == Sbuject.Langguage)
                LanguageScore(number);
            else if (sbu == Sbuject.Math)
                MathScore(number);
        }

但是这种方式是绝对不可取的。
原因有很多:比如:如果当学科较多时,你的 if -else 或者是 switch要写多长?这种方式很不利于扩展和代码的维护。

那么我们应该采取怎样的方式呢?

或者你可以这样考虑:如果我们在函数中,让第二个代表一种参数变量,这种变量,当将MathScore赋值给次变量时,那么这个变量就代表了MathScore方法;当将LanguageScore赋值给该参数变量时,该变量代表的就是LanguageScore方法。那么当我们执行该参数变量时,就是在执行MathScore或者LanguageScore方法。

我们来观察一下这两个有些类似的函数。首先返回类型的是:void ,参数是int和枚举类型。我们先看这里:

            int ans = 0;
            int num1 = 1;
            int num2 = 2;

如果我们让ans = num1,那么ans 就是1,如果让ans = num2,那么ans 就是2。从这里,我们可以想到一种方式:我们用X(没错,就是字母X ) 类比ans ,num1 和num2 分别类比MathScoreLanguageScore方法。如果X = MathScore() 函数(MathScore 函数类比为num 1 ),那么X 就表示MathScore函数;如果X = LanguageScore() 函数,那么X 就表示是LanguageScore函数了。那么我们如果执行了X,最后得到的结果就是我们想要执行的函数所显示的结果了。

那么此时这个X 就可以称作是:::一个委托!!!。(理解最重要,博主在这里用了点技巧,意思理解到位就行)。


那么我们根据上面的案例接着做:

首先我们需要一个委托,用于处理某个科目的函数或者多个科目函数的调用。

  public delegate void SubScore(int mumber);
然后将此委托类型作为 SayMyScores函数的参数,最后执行即可。我们看完整的代码:

namespace CSharpProject
{
    public delegate void SubScore(int mumber);
    class Program
    {  
        private static void SayMyScores(int number,SubScore subScore)
        {
            subScore(number);
        }
        public static void MathScore(int number)
        {
            Console.WriteLine("Math:" + number);
        }
        public static void LanguageScore(int number)
        {
            Console.WriteLine("Language:" + number);
        }
        static void Main(string[] args)
        {
            SayMyScores(60, MathScore);
            Console.ReadKey();

        }
    }
}
输出为:
Math:60

现在你已经对委托有了最简单最基础的了解了。那么我们在此总结下:

总结:

委托作为C#中的一个非常重要的概念是必须要掌握的。那么委托到底是什么呢?从数据结构层次来说,委托可以看做是一种用户自定义的类型。这种类型是专门处理具有相同签名和返回类型的。从设计的层次来讲,委托提供了方法的抽象。学过面向对象的应该可以理解这段话。

我认为,委托为一种类型,它是定义方法的类型,这样使得方法可以作为参数来进行传递。这种方式,很好的避免了代码中判断过多而且这种方式具有很好的扩展性。

那么委托既然可以作为一种类型,那么这种类型到底存储的是什么呢?简单理解:存储了具有相同签名和返回类型的方法的地址(跟C++的函数指针很是相似)。


ok,委托的概念已经将清楚了,下面就说下

委托的相关操作:

1.定义委托类型:

delegate void SubScore(int mumber);
委托类型声明:

delegate类型(公有|私有|保护) + delegate + 返回类型 + 函数名(函数参数);

2.声明委托变量:

 SubScore sub;
这里:委托既然可以看做是一种类型,那么我们就可以根据类变量的声明来进行,跟:int number 类似。

3.初始化委托变量:

初始化委托变量常用的有两种方式:

1)通过new 运算符:

  SubScore sub;
  sub = new SubScore(Program.MathScore);
2)通过直接赋值:
 SubScore sub;
 sub = new SubScore(Program.MathScore);
 sub = Program.LanguageScore;

4. 赋值委托:

需要注意的一点是委托是一种引用类型,那么我们可以直接将方法赋给它,然后如果你赋了很多方法,那么它只会引用最后赋值的方法,而对于其他的方法会被回收器回收:

 static void Main(string[] args)
        {
            SubScore sub;
            sub = new SubScore(Program.MathScore);
            sub = Program.LanguageScore;
            sub(60);
            Console.ReadKey();

        }
输出为:

Math:60

5.组合委托:

委托可以使用额外的运算符来组合。这个运算最终会创建一个新的委托,其调用列表是两个操作数的委托调用列表的副本的连接。
委托是恒定的,操作数委托创建后不会被改变。委托组合拷贝的是操作数的副本。

            SubScore sub1;
            SubScore sub2;
            SubScore sub3;
            sub1 = new SubScore(Program.MathScore);
            sub2 = Program.LanguageScore;
            sub3 = sub1 + sub2;
            sub3(61);
输出为:

Math:60

Language:60


6.委托的加减操作:

委托有时候可以看作是一种方法的数组,这样,我们就可以往数组里添加函数或者是删除函数:

利用加法可以让一个委托变量指向多个方法,那么当我们调用时,会顺序的调用委托所引用的所有方法
利用减法何以在指向的多个方法中删除某种方法:

 static void Main(string[] args)
        {
            SubScore sub1;
          
            sub1 = new SubScore(Program.MathScore);
            sub1 += Program.LanguageScore;
            sub1(61);
            Console.ReadKey();

        }

此时输出:

Math:61

Language:60

此时我们删除MathScore方法:

 static void Main(string[] args)
        {
            SubScore sub1;
          
            sub1 = new SubScore(Program.MathScore);
            sub1 += Program.LanguageScore;
            sub1(61);
            Console.WriteLine("删除后输出内容:");
               sub1 -= MathScore;
            sub1(60);
            Console.ReadKey();

        }

此时输出:


Math:61
Language:61
删除后输出内容:
Language:60

7.委托调用:

委托的调用和方法很是类似,委托调用后,调用列表中的每个方法都会被执行。但是在调用委托之前,应该判断下委托是否为空,如果委托为空的话会抛出异常。

 class Program
    {
        public delegate void SubScore(int number);
        public static void SayMyScores(int number,SubScore sub)
        {
            sub(number);
        }
        public static void MathScore(int number)
        {
            Console.WriteLine("Math:" + number);
        }
        public static void LanguageScore(int number)
        {
            Console.WriteLine("Language:" + number);
        }
        static void Main(string[] args)
        {
            SubScore sub = MathScore;
            sub += LanguageScore;
            if(null != sub)
            {
                sub(60);
            }
        }
    }
}

9.匿名方法:

匿名方法经常跟Lamabda结合使用,首先我简单先说下匿名方法(我会在最近更新一篇关于C#匿名方法的文章):

一般来说:在初始化委托时,可以内联一个方法,这个方法就是匿名方法:
如:

    class Program
    {
        public delegate int Score(int number);
        //public delegate void SubScore(int number);
        //public static void SayMyScores(int number,SubScore sub)
        //{
        //    sub(number);
        //}
        //public static void MathScore(int number)
        //{
        //    Console.WriteLine("Math:" + number);
        //}
        //public static void LanguageScore(int number)
        //{
        //    Console.WriteLine("Language:" + number);
        //}
        static void Main(string[] args)
        {
         
            Score myScore = delegate(int x)
            {
                return x;
            };
           Console.WriteLine(myScore(1));
        }
    }
}

输出为:

1

而当myScore 引用了其他的方法后,会发生什么呢?

这里我们实验一发:

 class Program
    {
        public delegate int Score(int number);
        public static int Say(int number)
        {
            Console.WriteLine("1111");
            return 0;
        }
       
        static void Main(string[] args)
        {
         
            Score myScore = delegate(int x)
            {
                return x;
            };
            myScore = Program.Say;
           Console.WriteLine(myScore(1));
        }
    }

输出为:

1111

0

看到这里就明白:会先调用引用的方法,然后调用本身的匿名方法。

关于委托还有很多重要的内容,这里就不一一列举了。等自己用熟练了,有了自己的深刻理解之后再分享给大家。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值