《C#高级编程》读书笔记(五):委托

1,委托的概念

当需要把方法传递给其他方法时,需要调用委托。委托只是一种特殊类型的对象,其特殊之处在于,我们之前定义的所有对象都包含数据,而委托包含的只是一个或多个方法的地址。

委托的类型安全性非常高。在定义委托时,必须给出它所表示的方法的签名和返回类型等全部细节。

2,使用委托的场景

给一个方法传递的参数也是方法,而且编译时不知道第二个方法是什么,这个信息只能在运行时得到,所以把第二个方法作为参数传递给第一个方法。

3,简单的例子

        public static string StrAddTwo(string x)
        {
            return x + 2;
        }

        public static string StrAddThree(string x)
        {
            return x + 3;
        }

        public delegate string StrAdd(string x);

        static void Main(string[] args)
        {
            StrAdd strAdd = StrAddTwo;
            Console.WriteLine(strAdd("Test"));
            strAdd = StrAddThree;
            Console.WriteLine(strAdd("Test"));

            Console.ReadKey();
        }

 4,更贴切点的例子

然而,委托并不是为了这些简单的操作而设计的,所以,必须用一个足够复杂的例子才能体现委托的必要。

首先,看一个给int数组进行冒泡排序的方法,代码如下:

        static void Main(string[] args)
        {
            int[] arr = new[] {5, 2, 4, 9, 15};
            Sort(arr);

            Console.ReadKey();
        }

        static void Sort(int[] sortArray)
        {
            bool swapped = true;
            do
            {
                swapped = false;
                for (int i = 0; i < sortArray.Length-1; i++)
                {
                    if (sortArray[i]>sortArray[i+1])
                    {
                        int temp = sortArray[i];
                        sortArray[i] = sortArray[i + 1];
                        sortArray[i + 1] = temp;
                        swapped = true;
                    }
                }
            } while (swapped);

            //查看排序结果
            foreach (var item in sortArray)
            {
               Console.WriteLine(item);
            }
        }

这个方法只适合于int类型,但我们希望Sort()方法能给任何对象排序,那么上面代码中的if (sortArray[i]>sortArray[i+1])就有问题了,因为它需要比较数组中的两个对象,看看哪一个更大。可以对int进行这样的比较,对如何对没有实现“<”运算符的新类进行比较?答案是能识别该类的客户端代码必须在委托中传递一个封装的方法,这个方法可以进行比较。

    public class BuddleSorter
    {
        public static void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison)
        {
            bool swapped;
            do
            {
                swapped = false;
                for (int i = 0; i < sortArray.Count-1; i++)
                {
                    if (comparison(sortArray[i + 1], sortArray[i]))
                    {
                        T temp = sortArray[i];
                        sortArray[i] = sortArray[i + 1];
                        sortArray[i + 1] = temp;
                        swapped = true;
                    }
                }
            } while (swapped);
        }
    }

再定义一个类,并实现它的comparison方法:

class Employee
    {
        public Employee(string name, decimal salary)
        {
            Name = name;
            Salary = salary;
        }
        public string Name { get; set; }
        public decimal Salary { get; set; }

        public override string ToString()
        {
            return string.Format($"{Name},{Salary:C}");
        }

        public static bool CompareSalary(Employee e1, Employee e2)
        {
            return e1.Salary < e2.Salary;
        }
    }

测试一下:

        static void Main(string[] args)
        {
            Employee[] employees =
             {
                new Employee("Bugs Bunny",20000),
                new Employee("Elmer Fudd",10000),
                new Employee("Daffy",25000),
                new Employee("Wile",100000.38m),
                new Employee("Forhorn",23000),
                new Employee("Road Runner",50000)
            };
            BuddleSorter.Sort(employees, Employee.CompareSalary);
            foreach (var employee in employees)
            {
                Console.WriteLine(employee);
            }

            Console.ReadKey();
        }

输出结果:

Elmer Fudd,¥10,000.00
Bugs Bunny,¥20,000.00
Forhorn,¥23,000.00
Daffy,¥25,000.00
Road Runner,¥50,000.00
Wile,¥100,000.38

5,多播委托

多播委托可以包含多个方法,这样的委托称为多播委托。如果调用多播委托,就可以按照顺序调用多个方法。示例代码:

Action<double> operations = MathOperations.MultiplyByTwo;
            operations += MathOperations.Square;
            ProcessAndDispayNumber(operations, 2.0);
            ProcessAndDispayNumber(operations,7.94);
            ProcessAndDispayNumber(operations,1.414);
            Console.ReadLine();

多播委托的缺点:如果其中一个方法,如果其中一个方法抛出异常,委托迭代将会终止,不会执行后面的方法。这时需要调用Delegate类定义的GetInvocationList()方法,它返回一个Delegate对象数组,现在可以使用这个委托调用与委托直接相关的方法,捕获异常,并继续下一次迭代。

6,匿名方法

匿名方法就是用作委托的参数的一段代码。如下所示:

string mid = ",middle part,";
            Func<string, string> anonDel = delegate(string param)
            {
                param += mid;
                param += " and this was added to the string.";
                return param;
            };
            Console.WriteLine(anonDel("Start of string"));

7,lambda表达式以及闭包

double d = 4;
Func<double,  double> oneParams = (x) => x * d;
Console.WriteLine(oneParams(3));
//输出12

 8,使用foreach语句的闭包

var values = new List<int>() {1,2,3};
            var funcs = new List<Func<int>>();

            foreach (var value in values)
            {
                funcs.Add(()=>value);
            }

            foreach (var f in funcs)
            {
                Console.WriteLine(f());
            }

输出:

1

2

3

这和c#4.0是不一样的,需要注意

9,事件

事件基于委托,为委托提供了一种发布/订阅机制。

时间: 2024-11-07 01:46:22

《C#高级编程》读书笔记(五):委托的相关文章

C++Windows核心编程读书笔记

转自:http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E6%96%87/71405.shtml "C++Windows核心编程读书笔记": 关键词:c++windows 核心 编程 读书笔记 这篇笔记是我在读<windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和windows实际机制可能有出入,但应该是合理的.开头几章由于我追求简洁

java 并发编程读书笔记

1.同步容器,同步容器包括Vector和HashTable,是早期jdk的一部分.另一部分是同步包装类,以Collections.synchronizedxxx的工厂方法创建. 2.同步容器虽然是线程安全的,但是对于复合操作,有时你可能需要加上额外的客户端加锁进行保护,即对于使用这些容器的客户端代码,如果存在复合操作,还是可能存在风险. 3.例如check-and-act操作.循环中的元素操作等,如果在客户端代码中没有额外的锁,都会发生意想不到的问题. 4.造成这些的问题都可以通过在客户端加锁来

C#高级编程(第8版)——委托声明、使用(第一节)

首先,声明一个Currency的结构.Currency结构有自己的ToString()重载方法和一个与GetCurrencyUnit()的签名相同的静态方法.这样,就可以用同一个委托变量调用这些方法了: struct Currency { public uint Dollars; public ushort Cents; public Currency(uint dollars, ushort cents) { this.Dollars = dollars; this.Cents = cents

读书笔记(委托)

委托回调静态方法和实例方法有何区别 当委托绑定静态方法时,内部的对象成员变量_target会被设置成null 当委托绑定实例方法是,_target将会设置成指向该实例方法所属的一个实例对象 当委托被执行时,该对象实例将会用来调用实例方法 1 class DelegateReturn 2 { 3 public delegate String GetStringDelegate(); 4 5 static string GetTypeName() 6 { 7 return typeof(Delega

TCP/IP网络编程读书笔记-简单的套接字编程(1)

在linux和windows下都是通过套接字编程进行网络编程.不同的系统上通信有部分差别,现在刚开始学习,给自己学习的时候一个总结. 一,socket函数的套接字步骤 第一,linux网络编程中接受连接请求(服务器端)套接字的四个步骤: 1)调用socket函数创建套接字 2)调用bind函数分配IP地址和端口号 3)调用listen函数转为可接收请求状态 4)调用accept函数受理连接请求 第二,linux网络编程中请求连接(客户端)套接字的两个步骤: 1)调用socket函数创建套接字 2

《算法导论》读书笔记(五)

摘要: 本章介绍了二叉查找树的概念及操作.主要内容包括二叉查找树的性质,如何在二叉查找树中查找最大值.最小值和给定的值,如何找出某一个元素的前驱和后继,如何在二叉查找树中进行插入和删除操作.在二叉查找树上执行这些基本操作的时间与树的高度成正比,一棵随机构造的二叉查找树的期望高度为O(lgn),从而基本动态集合的操作平均时间为θ(lgn). 1.二叉查找树 二叉查找树是按照二叉树结构来组织的,因此可以用二叉链表结构表示.二叉查找树中的关键字的存储方式满足的特征是:设x为二叉查找树中的一个结点.如果

《C#高级编程》笔记系列第三弹

我们在开发WinForm时,经常会看到partial关键字,比如,我们新建一个Windows Form时,后台代码自动添加如下: 1 public partial class Form1 : Form2 {3     public Form1()4     {5         InitializeComponent();6     }7 } <C#高级编程>书中说明:partial关键字允许把类.结构或接口放在多个文件中.一般情况下,一个类存储在单个文件中,但有时,多个开发人员需要访问同一个

UNIX环境高级编程学习笔记(第一章UNIX基础知识)

总所周知,UNIX环境高级编程是一本很经典的书,之前我粗略的看了一遍,感觉理解得不够深入. 听说写博客可以提高自己的水平,因此趁着这个机会我想把它重新看一遍,并把每一章的笔记写在博客里面. 我学习的时候使用的平台是Windows+VMware+debian,使用secureCRT来连接(可以实现多个终端连接). 因为第一章是本书大概的描述,所以第一章的我打算写得详细一点,而且书本的原话占的比例会比较多,重点的东西会用粗体显示出来. 1.1  引言 所有操作系统都为他们所运行的程序提供服务.典型的

javascript高级编程学习笔记(二)

写读书笔记的好处在于加深记忆,前一篇总结了编程中创建的对象的几种方式,以及常用的方式,这一篇总结实现继承的方式: 1.对象冒充: function ClassA(sColor) { this.color = sColor; this.sayColor = function () { alert(this.color); }; } function ClassB(sColor, sName) { this.newMethod = ClassA; this.newMethod(sColor); de

oracle sql 高级编程 历史笔记整理

20130909 周一 oracle sql 开发指南 第7章 高级查询 1.层次化查询select level,ttt.*,sys_connect_by_path(ttt.col1,',') from ttt start with .. Connect by prior -因为先建立树,再进行where过滤的.在where中过滤和在cooonect by中过滤是不一样的. 2.rollup cube高级查询 select grouping(col1) .. From ttt group by