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平台下,委托类型用来定义和相应应用程序中的回调.(回调?处理内存中两个实体双向通信的一种技术.) 第 ...
随机推荐
- ASP.NET Core 2.2中的Endpoint路由
Endpoint路由 在ASP.NET Core 2.2中,新增了一种路由,叫做Endpoint(终结点)路由.本文将以往的路由系统称为传统路由. 本文通过源码的方式介绍传统路由和Endpoint路由 ...
- Python模块——PrettyTable 模块
简介 PrettyTable 是python中的一个第三方库,可用来生成美观的ASCII格式的表格,十分实用. 安装 pip install prettytable 示例 从已有文件创建 CSV fr ...
- 用DDD模拟案例分析
之前我写了几篇关于DDD的介绍和一些小例子说明,我想这对于介绍DDD还是有些模糊,甚至还不知道怎么用DDD来分析设计.昨天和园友讨论也发现没有例子很难说明,所以今天我模拟了一个案例,同时这个案例也是真 ...
- 使用Project进行项目管理
下面开始介绍Project的使用. 1. 从下列地址获取Project 2010的副本. 版权问题,已删除地址. 2.安装 2.1 版权页 2.2 自定义安装页 2.3 安装完毕. 3.使用该软件进 ...
- 使用mybatis中的自定义TypeHandler处理PostgreSQL中的Json类型字段
业务扩展字段在PostgreSQL数据库中经常会使用json格式的数据来存储,然而mybatis默认是没有实现json类型字段对应的TypeHandler,所以一般我们需要自定义mybatis的Typ ...
- zmq setsockopt()
zmq.RCVTIMEO:在一个recv操作返回EAGAIN错误前的最大时间 设置socket的接收操作超时时间.如果属性值是0,zmq_recv(3)函数将会立刻返回,如果没有接收到任何消息,将会返 ...
- Golang GC原理
一.内存泄漏 内存泄露,是从操作系统的角度上来阐述的,形象的比喻就是“操作系统可提供给所有进程的存储空间(虚拟内存空间)正在被某个进程榨干”,导致的原因就是程序在运行的时候,会不断地动态开辟的存储空间 ...
- python 输出颜色与样式的方法
上次遇到这个问题就想写下来,其实当时我也不怎么会,老师说这个东西不需要理解,只需要死记硬背,写的多了就记住了,所以今天搜集了几篇文章,加上自己的理解,写下了这篇python 输出颜色的样式与方法的文章 ...
- springboot:Java模板引擎Thymeleaf介绍
Thymeleaf是一款用于渲染XML/XHTML/HTML5内容的模板引擎.类似JSP,Velocity,FreeMaker等,它也可以轻易的与Spring MVC等Web框架进行集成作为Web应用 ...
- [转]Angular4---部署---将Angular项目部署到IIS上
本文转自:https://www.cnblogs.com/kingkangstudy/p/7699710.html Angular项目部署到一个IIS服务器上 1.安装URL rewrite组件: 网 ...
