C#委托之我见
委托的使用方式很简单,了解一下基本语法就可以开撸了。但是使用委托的真正难题是不知道应用场景,就像习得了一门新功夫,但是却找不到任何施展拳脚的地方。这个难题一直困然着我,直到最近仿佛有所领悟,所以赶紧记下这可能尚不成熟的观点。如果有什么错误的想法,还望各位园友指正。
解耦合
其实委托最大的作用是解耦合,转移程序方法的功能定义方。在不使用委托的情况下,方法的功能和行为(能做的事)都是由方法提供方决定的,方法一经定义,能做的事情也就固定了,这就相当于方法是静态的。但是如果方法使用了委托参数类型,方法功能的定义方就发生了转移,此时方法能做什么事是由方法的调用方决定的。这样就相当于方法有了生命力,这种生命力是方法的调用方赋予的。并且方法的可重用性得到了提高,以前是做一件事情,现在是做一类事情。同时,委托可以看做是把方法作为方法的参数,这样会避免掉一些不必要的判断(因为作为参数的方法会定义做什么事情,不用再额外判断),简化程序逻辑。
Talk is cheap,show me the code.
方法作为方法的参数,避免掉不必要的判断
我们写程序时经常会遇到这样一种情况。在分支判断中,每个分支中做的操作都可以归属于一类事情,方法的签名也能保持一致。这时可以考虑使用委托消除掉这些分支判断。假设现在要做一个四则运算的功能,其拥有四个方法,它们的签名都相同,都接受两个double输入,并输出一个double。一般的做法是:
public enum Operate
{
Add,
Subtrac,
Multip,
Divisi
}
public static double Calculate(double a,double b, Operate operate)
{
switch(operate)
{
case Operate.Add:
return Add(a, b);
case Operate.Subtrac:
return Subtrac(a, b);
case Operate.Multip:
return Multip(a, b);
case Operate.Divisi:
return Divisi(a, b);
default:
return 0;
}
}
public static double Add(double a , double b)
{
return a + b;
}
public static double Subtrac(double a, double b)
{
return a - b;
}
public static double Multip(double a, double b)
{
return a * b;
}
public static double Divisi(double a, double b)
{
if (b == 0) throw new Exception("分母不能为0");
return a / b;
}
这样实现有一个缺点,现在是四则运算,万一以后加入其它类型的运算呢?每加入一个类型的运算都要新增一个分支判断,这样的话维护成本就有点高了,也不符合对修改关闭,对扩展开放的开闭原则。要是为每种类型的操作建个类,用多态的思想解决又有点小题大做了。可以考虑使用委托解决这个问题,使用和方法签名相同的委托代替枚举类型的参数。
首先新建一个和方法签名相同的委托类型,然后使用和方法签名相同的委托代替枚举类型的参数:
public delegate double CalculateDelegate(double a, double b);
public static double Calculate(double a, double b, CalculateDelegate operate)
{
return operate(a, b);
}
调用方决定具体的运算:
static void Main(string[] args)
{
Calculate(1, 2, Add);
Calculate(1, 1, Divisi);
}
利用委托来解决这种问题看似很好,但是也有缺点,需要为每一种计算类型定义相应的方法,而且其中有些方法使用频率并不高,程序中可能会大量出现这样的计算方法,维护这些方法反而是不小的负担。C#提供了匿名函数的方式来解决这个问题。
static void Main(string[] args)
{
Calculate(1, 2, delegate (double a,double b) { return a + b; });
Calculate(1, 1, delegate (double a, double b) { return a / b; });
}
嗯,解决了上面的问题。但是似乎代码可读性不够高,那就继续进化,C#提供了lambda表达式,让我们以几乎感觉不到委托存在的方式,顺其自然的使用C#委托,原生C#委托几乎被遗忘,委托三步走不复存在,委托=>匿名函数=>lambda表达式 究极进化,C#就是这么强大!
你可以这么玩: Calculate(1, 2, (double a,double b) => { return a + b; });
还可以这么玩: Calculate(1, 2, (a,b) => { return a + b; });
方法调用方决定方法做什么事
C#中的Linq可谓是将委托用到了极致,以Where方法为例,Where方法本身只负责筛选集合中的元素这类事,但是至于具体是哪件事,并不关心。具体做哪件事是由方法的调用方来指定的,比如筛选大于10的元素、或是小于5的元素,这些都是由调用方决定的。方法的灵活性、可重用性都得到了提高。设想一下,如果为每个元素筛选条件规则都去写一个除了筛选条件不同其他操作都相同的新方法,心态爆炸不?使用委托类型的参数,这一切将变得很简单。做一件事情变为做一类事情,至于是哪一件事情,方法调用方来决定喽。
这种方式最重要的应用就是回调函数。
回调函数就是一个通过函数指针调用的函数。 如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。 回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
简单理解,当我们将函数A传递给函数B,并由B来执行A时,A就成了一个回调函数(callback functions)。回调函数肯定是方法调用方负责定义的,当方法执行时,满足相应的条件就会触发此回调函数。在C#中实现回调函数的方式就是委托。
假设现在我们有两个方法,一个方法负责将数组中的每个元素翻倍,另一个方法负责加1,现在需要翻倍再加一。如果不使用委托(回调函数),则需要进行两次for循环,性能上无法接受,这个时候就可以使用委托(回调函数)来解决,只需要一次for循环就可以。
不使用委托(回调函数):
public static void Double(int[] nums)
{
for (int i = 0; i < nums.Length; i++)
{
nums[i] = nums[i] * 2;
}
}
public static void AddOne(int[] nums)
{
for (int i = 0; i < nums.Length; i++)
{
nums[i] = nums[i] + 1;
}
}
static void Main(string[] args)
{
int[] nums = { 1, 2, 3 };
Double(nums);
AddOne(nums);
}
使用委托(回调函数):
public static void DoubleAndAddOne(int[] nums,Func<int,int> func)
{
for (int i = 0; i < nums.Length; i++)
{
nums[i] = func(nums[i] * 2);
}
}
DoubleAndAddOne(nums, n => n + 1);
C#委托之我见的更多相关文章
- WPF DataBinding之我见
原创,转载请注明出处:WPF DataBinding之我见 一.DataBinding介绍 数据绑定是在应用程序 UI 与业务逻辑之间建立连接的过程. 如果绑定具有正确设置并且数据提供正确通知,则 ...
- DDD分层架构之我见
DDD分层架构之我见 前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定 ...
- 委托发展史(Linq操作符)
嗯~这篇就讲讲Linq吧! 之前讲过Lambda最后进化到了令人发指的地步: Func<string, int> returnLength; returnLength = text =&g ...
- Struts2 内核之我见
Struts2 内核之我见 完整分析 Struts2 内核中文文档 本文首先探讨了 Struts2 核心控制器的源码,以帮助解读 Struts2 的工作流程.接着讲解相关外围类.最后对 Struts ...
- HTML 事件(三) 事件流与事件委托
本篇主要介绍HTML DOM中的事件流和事件委托. 其他事件文章 1. HTML 事件(一) 事件的介绍 2. HTML 事件(二) 事件的注册与注销 3. HTML 事件(三) 事件流与事件委托 4 ...
- C#基础篇 - 理解委托和事件
1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针.简单理解,委托是一种可以把函数当做参数传递的类型.很多情况下,某 ...
- [.NET] C# 知识回顾 - 委托 delegate (续)
C# 知识回顾 - 委托 delegate (续) [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6046171.html 序 上篇<C# 知识回 ...
- [C#] C# 知识回顾 - 委托 delegate
C# 知识回顾 - 委托 delegate [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6031892.html 目录 What's 委托 委托的属性 ...
- 9、委托、事件、Lambda
开始 关于委托,肯定是要有问题的. 第一个问题,委托用来干什么? 看.net中的表述:在.net平台下,委托类型用来定义和相应应用程序中的回调.(回调?处理内存中两个实体双向通信的一种技术.) 第 ...
随机推荐
- 用vue-cli脚手架搭建一个仿网易云音乐的全家桶vue项目
一,vue-cli环境搭建 1,全局安装webpack npm install webpack -g 2,安装vue脚手架 npm install vue-cli -g 3,新建一个新的project ...
- Autowired byType 与 byName 策略
@Autowired是spring的注解,默认使用的是byType的方式向Bean里面注入相应的Bean.例如: @Autowiredprivate UserService userService;这 ...
- python语法基础-初始化/继承
写了一些程序,基本上都是直接def函数 然后在main方法中 调用 但是在一些应用程序中 会有基本语法的使用(初始化,继承) 初始化: 1.在程序执行时一定执行一次的操作 2.python中初始化in ...
- github相关指令学习
正在廖雪峰官网学习关于git的相关知识,已经不是第一次来学习,但是忘得太快,索性这次边学边记录笔记,加深记忆,方便后期查看 1.找到一个合适的地方,鼠标右键 Git Bush Here ,新建文件夹, ...
- Jwt Token 安全策略使用 ECDSA 椭圆曲线加密算法签名/验证
椭圆曲线密码学(Elliptic curve cryptography),简称 ECC,是一种建立公开密钥加密的算法,也就是非对称加密,ECDH 与 ECDSA 是基于 ECC 的算法.类似的还有 R ...
- 829. 连续整数求和-leetcode
题目:给定一个正整数 N,试求有多少组连续正整数满足所有数字之和为 N? 示例 1: 输入: 5 输出: 2 解释: 5 = 5 = 2 + 3,共有两组连续整数([5],[2,3])求和后为 5. ...
- How to translate virtual to physical addresses through /proc/pid/pagemap
墙外通道:http://fivelinesofcode.blogspot.com/2014/03/how-to-translate-virtual-to-physical.html I current ...
- 程序员进阶之算法练习:LeetCode专场
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由落影发表 前言 LeetCode上的题目是大公司面试常见的算法题,今天的目标是拿下5道算法题: 题目1是基于链表的大数加法,既考察基本 ...
- Maven_1 安装配置
所需工具 : JDK 1.8 Maven 3.3.9 Windows 7 下载Maven 3.3.9 http://maven.apache.org/download.cgi 首先要先安装JDK. ...
- ASP.NET MVC 5 Authentication Breakdown
In my previous post, "ASP.NET MVC 5 Authentication Breakdown", I broke down all the parts ...
