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平台下,委托类型用来定义和相应应用程序中的回调.(回调?处理内存中两个实体双向通信的一种技术.) 第 ...
随机推荐
- JavaScript中原型链的那些事
引言 在面向对象的语言中继承是非常重要的概念,许多面向对象语言都支持两种继承方式:接口继承和实现继承.接口继承制只继承方法签名,而实现继承继承实际的方法.在ECMAScript中函数没有签名,所以EC ...
- Java RMI 概观
RMI是Java的一组拥护开发分布式应用程序的API. RMI使用Java语言接口定义了远程对象,它集合了Java序列化和Java远程方法协议(Java Remote Method Protocol) ...
- 从零开始学 Web 之 Ajax(五)同步异步请求,数据格式
大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...
- Spring @RequestAttribute
@RequestAttribute注解用法 @RequestAttribute用在方法入参上,作用:从request中取对应的值,至于request中是怎么存在该属性的,方式多种多样,拦截器中预存.M ...
- Select2插件的隐藏、设置宽度
<select id="selPrinvice" class="Select2 select2-hidden-accessible" style=&quo ...
- sql 去掉 空格
一.问题描述: 数据库中字段 nvarchar类型 存放数据如下: '3.3×10' 二.解决方案: --测试用例CREATE TABLE #temp(NAME NVARCHAR(20) null) ...
- Mysql常用函数总结(二)
有的时候们需要了解当前的时间,这时候我们就可以调用时间函数了.下面就是一些常用的时间函数下面来说一下DATE_FORMAT(date,format):format可以有以下格式符:举个例子吧! mys ...
- Android基础系列合集
四大组件 1.Service Android四大组件-Service Android-远程Service Service 动态更新 UI 2.Activity Android四大组件-Activity ...
- Django Rest Framework之解析器
基本代码结构 urls.py: from django.conf.urls import url, include from web.views.s5_parser import TestView u ...
- Graphviz的安装 - windows环境下
1. 官网下载 http://www.graphviz.org/ 往下拉,选择这一个 点进去,选择msi文件下载 下载完成之后,直接双击运行即可 安装完成之后要配置环境变量 2. 配置环境变量 将gr ...
