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平台下,委托类型用来定义和相应应用程序中的回调.(回调?处理内存中两个实体双向通信的一种技术.) 第 ...
随机推荐
- LeetCode--No.009 Palindrome Number
9. Palindrome Number Total Accepted: 136330 Total Submissions: 418995 Difficulty: Easy Determine whe ...
- 从零开始学 Spring Boot
1.下载 spring-tool-suite https://spring.io/tools3/sts/legacy 2.解压运行 sts-bundle\sts-3.9.7.RELEASE\STS.e ...
- “大话架构”阿里架构师分享Java程序员需要突破的技术要点
一.源码分析 源码分析是一种临界知识,掌握了这种临界知识,能不变应万变,源码分析对于很多人来说很枯燥,生涩难懂. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 我认为是阅读源码的最核心 ...
- windows下更新node环境
https://github.com/Kenshin/gnvm 下载gnvm.exe程序 使用where node命令查看node所在目录,并将下载好的gnvm.exe程序复制到目录中 输入gnvm ...
- Python 解析har 文件将域名分类导出
前言 作为程序员平时主要是使用 shadowsocks 作为代理工具的.shadowsocks 有个很明显的优点儿就是可以设置白名单和黑名单.白名单是会走shadowsocks的自动代理模式. 遇到的 ...
- Deep Reinforcement Learning: Pong from Pixels
这是一篇迟来很久的关于增强学习(Reinforcement Learning, RL)博文.增强学习最近非常火!你一定有所了解,现在的计算机能不但能够被全自动地训练去玩儿ATARI(译注:一种游戏机) ...
- Redis缓存使用技巧
缓存能够有效加速应用的访问速度,同时可以降低后端负载,在应用架构中起着至关重要的作用,本文主要介绍缓存使用的一些技巧. 缓存更新策略 LRU/LFU/FIFO算法剔除 场景:数据一致性要求较低 原理: ...
- 将应用代码由eclipse导入Android studio的方法NDK-Build和Cmake两种方法(以android_serialport_api为例)
网上翻了几百篇博客,看了半天,要不就是写的乱七八糟看不懂,要不就是隐藏了一些细节,要不就是实现不了,最后还是在Android官网上看明白了,而且说得有条有理,以后遇到不懂的一定要先翻官网. 参考资料: ...
- Python 的几种推导式
推导式 comprehensions(又称解析式):是 Python 中很强大的.很受欢迎的特性,具有语言简洁,速度快等优点.推导式包括: 1. 列表推导式 2. 字典推导式 3. 集合推导式 对以上 ...
- [转]Node.js tutorial in Visual Studio Code
本文转自:https://code.visualstudio.com/docs/nodejs/nodejs-tutorial Node.js tutorial in Visual Studio Cod ...