C#基础篇——委托

前言

在本章中,主要是借机这个C#基础篇的系列整理过去的学习笔记、归纳总结并更加理解透彻。

在.Net开发中,我们经常会遇到并使用过委托,如果能灵活的掌握并加以使用会使你在编程中游刃有余,然后对于很多接触C#时间不长的开发者而言,较好的理解委托和事件并不容易。

本节主要是讲述对委托的定义、委托的使用、多播委托、泛型委托、匿名方法、Func和Action委托、Lambda委托,并对它们进行讨论。

说明

简单说它就是一个能把方法当参数传递的对象,而且还知道怎么调用这个方法,同时也是粒度最小的“接口”(约束了指向方法的签名)。

开始

1.定义委托

委托:是一种定义方法签名的类型。 当实例化委托时,可以将其实例与任何具有兼容签名的方法相关联。 可以通过委托实例调用方法。

这里引用一个网友的说法:

某人有三子,让他们各自带一样东西出门,并带回一头猎物。
上面一句话可以理解为父亲对儿子的委托:猎物 办法(工具 某工具)-->delegate 猎物(返回值) 带回猎物(委托名)(工具(参数类型) x)-->delegate int GetValue(int i)
三个人执行委托的方法各不相同
兔子 打猎(工具 弓)-public static int GetValue1(int i){ return i; }
野鸡 买(工具 钱)-public static int GetValue2(int i){ return i*2; }
狼 诱捕(工具 陷阱)-public static int GetValue3(int i){ return i*i; }

2.简单的使用

一个委托类型定义了该类型的实例化时能调用的一类方法,这些方法含有同样的返回类型和同样参数(类型和参数个数相同)

比如:定义一个委托

delegate int Calculator (int x);

此委托适用于有着int返回类型和一个int类型参数方法。

static int Double (int x) { return x * 2; }

创建一个委托实例,并将方法赋值给委托实例

Calculator c = new Calculator(Double);
//或者另一种写法
Calculator c = Double;

通过委托实例的调用

int result = c(2);

3.多播委托

在开发中,我们有时候会遇到要通过调用一个委托,同时可以执行多个方法的时候,就可以考虑用多播委托。调用多个委托需要多次显示调用这个委托。所有的委托实例都可以包含多个方法,实现多播功能。

这个打个比方:多播,就像一群程序员在瞬聘网填好了求职意向后,某天有个公司发布了一个和这些程序员求职意向刚好相匹配的工作,然后这些求职者都被通知了 - “有一份好工作招人啦,你们可以直接申请去上班了!”。
也就是说,一个委托实例不仅可以指向一个方法,还可以指向多个方法。
多播委托,提供了一种类似于流水线的钩子机制,只要加载到这条流水线上的委托,都会被顺序执行。因为所有的都继承自MulticastDelegate,因此所有的委托都具有多播特性
        //声明一个委托,委托返回值为void
        public delegate void Greetings(String name);

        public static void Hello(String name)
        {
            Console.WriteLine("您好, {0}!", name);
        }

        public static void GoodBye(String name)
        {
            Console.WriteLine("再见, {0}!", name);
        }

        public static void Main()
        {
            Greetings greetings = Hello;
            //使用+=给委托添加方法
            greetings += GoodBye;
            String name = "艾三元";
            Console.WriteLine("这是一种调用方法:");
            //第一种执行方式
            greetings(name);
            //第二种执行方式
            Console.WriteLine("这是另一种使用方法");
            //返回委托的调用列表。
            Delegate[] delegates = greetings.GetInvocationList();
            //注意这里的delegates列表中存储的是Greetings类型的委托
            foreach (Greetings greeting in delegates)
            {
                greeting(name);
            }
            Console.ReadKey();
        }

1748233452

说明:

  • 如果是多播委托,委托的签名就必须返回 void ,否则,返回值应送到何处?当委托只包含一个方法的时候,则可以通过所封装的方法发现其返回类型的声明,不一定必须是void。实际上,如果编译器发现某个委托返回 void ,就会自动假定这是一个多播委托。

  • “+=” 用来添加,“-=”用来从委托中删除方法调用

4.泛型委托

在之前的篇章中,我们已经学会了什么是泛型,因此,也方便我们理解泛型委托,简单的说,就是一种含有泛型参数的委托。

		public delegate T Calculator<T>(T arg);
        static int Double(int x) { return x * 2; }

        static class Utility
        {
            public static void Calculate<T>(T[] values, Calculator<T> c)
            {
                for (int i = 0; i < values.Length; i++)
                    values[i] = c(values[i]);
            }
        }
        static void Main(string[] args)
        {
            int[] values = { 11, 22, 33, 44 };

            Utility.Calculate(values, Double);
            foreach (int i in values)
                Console.Write(i + " "); // 22 44 66 88
            Console.ReadKey();
        }

5. 匿名方法

匿名方法,是在初始化委托时候内联声明的方法。

每次实例化一个委托时,都需要事先定义一个委托所要调用的方法。为了简化这个流程,C# 2.0开始提供匿名方法来实例化委托。这样,我们在实例化委托时就可以 “随用随写” 它的实例方法。

    static string GetNumber(string str)
    {
        return str;
    }
    delegate string DelNumber(string str);
    static void Main(string[] args)
    {
        //声明一个名称为GetNumber的具名方法
        DelNumber delNumber1 = GetNumber;
        Console.WriteLine(delNumber1("这是具名方法"));

        //匿名方法 ,未在别的地方定义方法,而是直接把方法写在实例化代码中
        DelNumber delNumber2 = delegate (string str)
        {
            return str;
        };
        Console.WriteLine(delNumber2("这是匿名方法调用"));
        Console.ReadKey();
    }

    #endregion

通过以上简单的示例看出:

匿名方法的语法:关键字delegate {参数列表}{语句块}

delegte { Paramters} {ImplementationCode}

		delegate (string str)
        {
            return str;
        };

使用的格式是:

委托类名 委托实例名 = delegate (args) {方法体代码} ;

		 delegate string DelNumber(string str); //委托类型的返回类型
        //匿名方法 ,未在别的地方定义方法,而是直接把方法写在实例化代码中
        DelNumber delNumber2 = delegate (string str)
        {
            return str;                         //根据返回类型,返回一个string类型
        };

这样就可以直接把方法写在实例化代码中,不必在另一个地方定义方法。当然,匿名委托不适合需要采用多个方法的委托的定义。需要说明的是,匿名方法并不是真的“没有名字”的,而是编译器为我们自动取一个名字。

可以在以下地方使用匿名方法:

  • 声明委托变量时为初始化表达式。
  • 组合委托时在赋值语句的右边。
  • 为委托增加事件时在赋值语句的右边。

6.Func 和 Action 委托

在之前,我们在使用委托的时候,都是自定义一个委托类型,再使用这个自定定义的委托定义一个委托字段或变量。而在后续的编程语言中又新加入了一种特性,C#语言预先为我们定义了两个常用的委托,一个是Func,一个是Action,还带来了Lambda,这使得委托的定义和使用变得简单起来, 在以后进行C#程序编写中引入委托更加灵活。

Action委托

C#中与预定义了一个委托类型Action,基本特点就是可以执行一个没有返回值,没有参数的方法。是一类没有输出参数的委托,但是输入参数可以为C#中的任意类型,即可以进行委托执行形式的方法。

    static void printString()
    {
        Console.WriteLine("Hello World");
    }
    static void printNumber(int x)
    {
        Console.WriteLine(x);
    }
    static void Main(String[] args)
    {
        //Action基本使用
        Action a = printString;
        a(); // 输出结果  Hello World
        //Action指向有参数的方法
        Action<int> b = printNumber; // 定义一个指向 形参为int的函C#数
        b(5); // 输出结果  5
    }

Action可以通过泛型来指定,指向的方法有 0 - 16个参数

Action<int, int, string, bool 等等>

Func委托

Func同样也是预定的委托,是一种由返回值的委托,传递0-16个参数,其中输入参数和返回值都用泛型表示。

  		static int GetNumber()
        {
            return 1;
        }
        static int GetNumber(string str)
        {
            return 1;
        }
        static void Main(string[] args)
        {
            Func<int> a = GetNumber; // 定义一个Func 委托,  指向一个返回int类型的 方法
            Console.WriteLine(a());
            Func<string, int> b = GetNumber; // 泛型中最后一个参数表示返回值类型。
            Console.WriteLine(b("Hello"));
        }	

注意:Func<string, int> 最后一个参数表示返回值类型,前面的都是形参类型。

7. Lambda表达式

江山代有才人出,纵然匿名方法使用很方便,可惜她很快就成了过气网红,没能领多长时间的风骚。如今已经很少见到了,因为delegate关键字限制了她用途的扩展。自从C# 3.0开始,她就被Lambda表达式取代,而且Lambda表达式用起来更简单。Lambda表达式本质上是改进的匿名方法。

在匿名方法中,delegate关键字有点多余,因为编译器已知将我们的方法赋值给委托。因此,我们很容易的将匿名方法的步骤转换为Lambda表达式:1. 删除delegate关键字。2.在参数列表和匿名方法主体之间放lambda运算符=>。

DelNumber delNumber2 = delegate (string str){ return str;}; //匿名方法

DelNumber delNumber2 =  (string str) =>{ return str;}; //Lambda方法

Lambda表达式的灵感来源于数学中的Lambda积分函数表达式,例如下图:

1270751451

Lambda表达式把其中的箭头用 => 符号表示。

上面的对比例子中,Lambda还可以进一步简化

delegate string DelNumber(string str); //委托类型的返回类型
DelNumber delNumber2 =  (string str) =>{ return str;}; //Lambda方法
DelNumber delNumber3 =         (str) =>{ return str;}; //省略类型参数
DelNumber delNumber4 =           str =>{ return str;}; //省略类型参数( 如果只有一个隐式类型参数,可以省略周围的圆括号)
DelNumber delNumber5 =           str =>  str; //语句块替换为return关键字后的表达式 ( 如果只有一个返回语句,可以将语句块替换为return关键字后的表达式)

如今Lambda表达式已经应用在很多地方了,例如方法体表达式(Expression-Bodied Methods)、自动只读属性表达式等等。

Lambda表达式形式上分为两种:

1.表达式Lambda
当匿名函数只有一行代码时,可采用这种形式。例如:

DelNumber delNumber= (s4, s5) => s4.Age <= s5.Age;

其中=>符号代表Lambda表达式,它的左侧是参数,右侧是要返回或执行的语句。参数要放在圆括号中,若只有一个参数,为了方便起见可省略圆括号。有多个参数或者没有参数时,不可省略圆括号。

相比匿名函数,在表达式Lambda中,方法体的花括号{}和return关键字被省略掉了。

用的也是表达式Lambda,这是Lambda表达式的推广, 是C# 6 编译器提供的一个语法糖。

2.语句Lambda
当匿名函数有多行代码时,只能采用语句Lambda。例如,上面的表达式Lambda可改写为语句Lambda:

DelNumber delNumber= (s4, s5) => 
{
    //此处省略其他代码
    return s4.Age <= s5.Age;
};

语句Lambda不可以省略{}和return语句。

完整示例

        delegate string DelNumber(string str); //委托类型的返回类型
        static void Main(string[] args)
        {

            DelNumber delNumber2 = (string str) => { return str; }; //Lambda方法
            DelNumber delNumber3 = (str) => { return str; }; //省略类型参数
            DelNumber delNumber4 = str => { return str; }; //省略类型参数( 如果只有一个隐式类型参数,可以省略周围的圆括号)
            DelNumber delNumber5 = str => str; //语句块替换为return关键字后的表达式 ( 如果只有一个返回语句,可以将语句块替换为return关键字后的表达式)

            Console.WriteLine(delNumber2("lambda"));

            Console.WriteLine(delNumber3("lambda"));
            Console.WriteLine(delNumber4("lambda"));
            Console.WriteLine(delNumber5("lambda"));
            Console.ReadKey();
        }

注意:一个参数可以省略圆括号,多个参数必须圆括号,但是没有参数,必须使用一组空的圆括号

如: (参数,参数)=>{语句} 或者 表达式
       (参数)  =>{语句} 或者 表达式
        参数   =>{语句} 或者 表达式
        ()    =>{语句} 或者 表达式

总结

  1. 委托相当于用方法作为另一方法参数,同时,也可以实现在两个不能直接调用的方法中做桥梁,如在多线程中的跨线程的方法调用就得用委托。
  2. 熟悉在什么情况使用委托,在使用事件设计模式时,当需要封装静态方法时,当需要方便的组合时等多种情况下,可以加以使用。
  3. 如果有不对的或不理解的地方,希望大家可以多多指正,提出问题,一起讨论,不断学习,共同进步。
  4. 在下一节中,将对事件进行简单介绍,并总结归纳。

参考 文档 《C#图解教程》

注:搜索关注公众号【DotNet技术谷】--回复【C#图解】,可获取 C#图解教程文件

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《轻松学C#(图解版)》完整扫描版================================================================ 基本信息 作者:谷涛、扶晓、毕国锋 丛书名:轻松学开发 出版社:电子工业出版社 ISBN:978-7-121-20223-0 出版日期:2013年6月 开本:16开 页码:408页 版次:1-1 定价:¥55.00 所属分类:计算机 > 软件与程序设计 > C# ================================================================ 内容简介 本书由浅入深,全面、系统地介绍了C#程序设计。除了详细地讲解C#知识点外,本书还提供了大量的实例,供读者实战演练。本书共分三。第一C#概述,主要介绍的是Visual Studio 2012的开发环境及搭建。第二是面向对象基础,主要介绍类、对象、字段、方法、流程控制、数组、继承、属性、运算符重载、接口等C#基本内容。第三是应用技术,主要介绍的是异常处理、文件和流、委托、事件、Lambda表达式、命名空间、预处理器、程序集、运行时类型标识、反射、特性、泛型、LINQ和数据库开发等。 ================================================================ 图书目录 第一 C#概述 第1章 C#入门 2 1.1 C#概述 2 1.1.1 C#的发展 2 1.1.2 C#开发的基础 2 1.2 搭建开发环境 3 1.2.1 Visual Studio 2012软硬件配置要求 3 1.2.2 下载Visual Studio 2012 3 1.2.3 安装Visual Studio 2012 4 1.2.4 初始化配置 7 1.3 第一个程序—Hello World 8 1.4 小结 11 1.5 习题 12 第二 面向对象基础 第2章 类和对象 16 2.1 分析Hello World程序 16 2.2 语法规范 17 2.2.1 标识符 17 2.2.2 关键字 18 2.2.3 注释 19 2.3 定义类 20 2.4 实例化对象 20 2.5 小结 20 2.6 习题 21 第3章 定义类——字段 23 3.1 数据类型 23 3.1.1 简单值类型 23 3.1.2 值的表示——字面量 26 3.1.3 转义序列 27 3.2 定义字段 27 3.2.1 定义字段 28 3.2.2 静态字段和实例字段的访问 28 3.2.3 字段初始化 29 3.2.4 字段的动态赋值——Read()和ReadLine() 31 3.2.5 字段输出 31 3.2.6 格式化输出 32 3.2.7 数据类型转换 39 3.2.8 只读字段 41 3.2.9 访问控制 41 3.3 运算符 43 3.3.1 算术运算符 43 3.3.2 自增、自减运算符 44 3.3.3 复合赋值运算符 45 3.3.4 位运算符 46 3.3.5 sizeof运算符 47 3.3.6 运算符的优先级 47 3.4 小结 49 3.5 习题 49 第4章 定义类——方法 51 4.1 方法的概述 51 4.1.1 定义方法 51 4.1.2 方法的调用 52 4.2 方法体的构成 53 4.2.1 局部变量 53 4.2.2 局部变量与字段同名的解决——this关键字 54 4.2.3 语句 55 4.2.4 全局变量 55 4.3 返回值 56 4.3.1 返回值类型 56 4.3.2 从方法返回 57 4.4 参数 58 4.4.1 参数的分类 58 4.4.2 引用参数(ref参数) 59 4.4.3 输出参数(out参数) 61 4.5 特殊的方法 62 4.5

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值