• 「C#学习笔记」委托(上)


    委托我是看B站杨旭大佬(B站ID:软件工艺师)的视频学的。不过视频给的例子不多,讲解也比较硬核,所以我编了一些例子来加深理解。

    蒟蒻我也是初学,所以如果有错误的地方,还请不吝指正。

    代码都是在本地跑过一遍的,应该没有大问题。

    这一篇中凡是塞到委托里的方法,都是静态方法。实例方法(可能)会在下一章里讲(如果有的话∠( ᐛ 」∠)_)。

    委托是C#中的一个类,很像C/C++中的函数指针。

    定义委托

    public delegate int Transformer(int x);
    

    delegate是委托的意思。

    第一个int表示,Transformer类的委托,其返回值必须是int

    后面括号里的int x表示,Transformer类的委托,其传入变量是一个int

    举个例子

    代码如下:

    using System;
    
    namespace LearnDemo
    {
        public delegate int Transformer(int x);
    
        class Program
        {
            static int Square(int u)
            {
                return u * u;
            }
    
            static void Main(string[] args)
            {
                Transformer mysquare = Square;
    
                string input = Console.ReadLine();
                int sideLength = Convert.ToInt32(input);
    
                Console.WriteLine("The area of a square is {0}.", mysquare(sideLength));
            }
        }
    }
    

    这里首先定义了一个委托,名为Transformer。根据它的定义,Transformer类接受的方法返回值必须是一个int,而且传入一个int

    我们首先在Program类里定义了一个符合上述条件的静态方法——int Square(int u)。然后,我们在Main中定义了一个委托mysquare,并将方法Square传入。

    然后,我们就可以在后面直接调用mysquare(参数)来调用Square方法。

    上面这个例子只是简单的说明了委托的用法。当然按照上面的场景,我们完全不需要拐弯抹角地用委托,而在Main中直接调用Square就可以了。

    PS:上面的代码有两处简写:

    1. Transformer mysquare = Square;,完整的写法是这样的:Transformer mysquare = new Transformer(Square);
    2. mysquare(sideLength);,完整的写法是这样的:mysquare.Invoke(sideLength);

    开头说过委托很像C/C++的函数指针。所以,委托的作用之一就是可以作为参数去传递。

    作为参数传递

    还是看个例子,给数组排序:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace LearnDemo
    {
        // 类型名啥的都是乱起的,不要在意
        public delegate bool Compare<T>(T value1, T value2);
    
        class Program
        {
            // 就是个简单的插入排序,不用在意细节
            private static void MySort<T>(T[] list, Compare<T> cmp)
            {
                for (int i = 0; i < list.Length; i++)
                {
                    int bigcur = i;
                    for (int j = i; j < list.Length; j++)
                    {
                        if (cmp(list[j], list[bigcur])) bigcur = j;
                    }
    
                    T temp = list[i];
                    list[i] = list[bigcur];
                    list[bigcur] = temp;
                }
            }
    
            // 就是个简单的比较大小
            private static bool cmpint(int a, int b)
            {
                if (a < b) return true;
                else return false;
            }
    
            static void Main(string[] args)
            {
                string input;
    
                // 输入一行数字,用空格分开
                input = Console.ReadLine();
                string[] strarr = input.Split(' ');
    
                // 把这一行数塞到int数组里
                int[] a = new int[strarr.Length];
                for (int i = 0; i < strarr.Length; i++)
                    a[i] = Convert.ToInt32(strarr[i]);
    
                // 定义委托
                Compare<int> cmp = cmpint;
                // 这里通过向MySort传递委托cmp来传递数组a排序的比较规则
                // 虽然对于int来说完全不必这么做233333333
                MySort(a, cmp);
    
                // 输出
                foreach (var i in a)
                    Console.Write(i + " ");
            }
        }
    }
    

    如果你熟悉C++,那么这个用法你肯定不陌生。这里和C++向sort函数里传递比较函数的用法完全一样(只不过我懒得写快速排序所以就写了插入排序哈哈哈哈)。

    当然,给int数组排序用不着这么麻烦,但对于相对复杂的classstruct来说,我们就可以通过委托来制定相对复杂的比较规则。

    委托多播

    我个人理解的委托多播是指,委托可以把多个方法组合起来。使用方法如下:

    SomeDelegate delegateName = Method1;
    delegateName += Method2;
    delegateName += Method3;
    

    这样,当我们调用delegateName的时候,程序就会依此执行Method1, Method2, Method3

    同样,委托也支持-=操作。比如,我们执行delegateName -= Method2,这样,显然delegateName中还有Method1Method3。这个时候,我们再调用delegateName的时候,它就会依次执行Method1, Method3

    如果delegateName只剩下Method1了,如果我们再执行delegateName -= Method1,那么delegateName就会变成null。此时不能直接调用这个委托,否则会出错。

    PS:这里提一句,我们执行+=-=的时候,程序实际上是重新创建了一个委托,然后在原来委托的基础上加上(去掉)新的委托,然后销毁原来的委托。

    举个复杂一点的例子

    比如,我们输入若干学生的ID和成绩,要求我们按照ID顺序输出每个学生的名次,我们就可以把按照成绩排序、给学生排名、按照ID排序塞到一个委托多播里。这样,我们就可以直接调用这一个委托来全部执行这些操作。(其实本人也是初学,实在想不到更好的例子了哈哈哈)

    using System;
    
    namespace LearnDemo
    {
        // Student的定义。因为一共就3个int所以就定义成struct了
        struct Student
        {
            public int id, score, rank;
        }
    
        class Program
        {
            // 为了方便没写成泛型,而且只是为了举多播的例子也没必要
            private delegate void StudentOperation(Student[] list);
    
            // Swap是为了排序写的,可以忽略
            static private void Swap<T>(ref T a, ref T b)
            {
                T temp = a;
                a = b;
                b = temp;
            }
    
            // 按照成绩排序,细节可以忽略
            static void SortByScore(Student[] students)
            {
                for (int i = 0; i < students.Length; i++)
                {
                    int cur = i;
                    for (int j = i + 1; j < students.Length; j++)
                    {
                        if (students[j].score > students[cur].score)
                            cur = j;
                    }
    
                    Swap(ref students[i], ref students[cur]);
                }
            }
    
            // 赋予排名,细节可以忽略
            static void AssignRank(Student[] students)
            {
                for (int i = 0; i < students.Length; i++)
                    students[i].rank = i + 1;
            }
    
            // 按照Id排序,细节可以忽略
            static void SortById(Student[] students)
            {
                for (int i = 0; i < students.Length; i++)
                {
                    int cur = i;
                    for (int j = i + 1; j < students.Length; j++)
                    {
                        if (students[j].id < students[cur].id)
                            cur = j;
                    }
    
                    Swap(ref students[i], ref students[cur]);
                }
            }
    
            static void Main(string[] args)
            {
                // 输入的引导和提示,可以忽略
                Console.WriteLine("Please input the number of students:");
                int num = int.Parse(Console.ReadLine());
    
                Console.WriteLine("Then input {0} pairs of student informations, " +
                    "each pair contains student id and score, seperate them by one space please.", num);
                Student[] students = new Student[num];
    
                // 把字符串转换成数字,可以忽略
                for (int i = 0; i < num; i++)
                {
                    string[] str2 = Console.ReadLine().Split(' ');
                    students[i].id = int.Parse(str2[0]);
                    students[i].score = int.Parse(str2[1]);
                }
    
                // 创建委托多播
                StudentOperation operation = new StudentOperation(SortByScore);
                operation = operation + AssignRank + SortById;
    
                // 调用委托多播
                operation(students);
    
                // 输出
                Console.WriteLine("Rank List:");
                foreach (var i in students)
                {
                    Console.WriteLine("ID: {0}, score: {1}, rank: {2}", i.id, i.score, i.rank);
                }
            }
        }
    }
    

    至少,我们可以复用operation(实在想不到更好的原因了,就写一写练练手吧2333333)。

    关于委托多播的一些坑?

    PS:虽然说,委托多播是把一系列的方法结合了起来,但是它只返回最后一个函数的返回值。前面的方法会被调用,但是其返回值就直接被弃用了。

    C#会把委托的+, +=, -, -=编译成System.DelegateCombineRemove两个方法。

  • 相关阅读:
    bryce1010的图像处理课程设计
    linux常用命令
    linux自动连接校园网设置
    LoadRunner12学习之路(6-8)
    LoadRunner12学习之路(1-5)
    Windows软件推荐
    LoadRunner_11破解教程完整版
    linux的SHELL编程
    菜鸡CodeFoces打卡单
    21天记完托福单词打卡
  • 原文地址:https://www.cnblogs.com/icysky/p/13965828.html
Copyright © 2020-2023  润新知