语言的设计,真的是挺有意思的。第一次看这个代码[1]时,旁人随口了一句“哇,好多实心句号”。

  当时马上一个想法是——怎么实现的?返回了对象,然后再调用方法?然后就放下了,后来发现,这个是真值得说一说的。

            var sim = new InputSimulator();
sim.Keyboard
.ModifiedKeyStroke(VirtualKeyCode.LWIN, VirtualKeyCode.VK_R)
.Sleep()
.TextEntry("notepad")
.Sleep()
.KeyPress(VirtualKeyCode.RETURN)
.Sleep()
.TextEntry("These are your orders if you choose to accept them...")
.TextEntry("This message will self destruct in 5 seconds.")
.Sleep()
.ModifiedKeyStroke(VirtualKeyCode.MENU, VirtualKeyCode.SPACE)
.KeyPress(VirtualKeyCode.DOWN)
.KeyPress(VirtualKeyCode.RETURN);

1. 神奇的链接(chaining)

1.1 拓展方法

  想了很久该怎么引入话题,或者这样说,像这种写法,

  if (str == null || str == “”)

  相信刚刚开始编程的时候都这样写过,而C#语言规则里面,告诉我们可以这样写:

  string.IsNullOrEmpty(str);

  后来,有一天,我很自然地就写成这样了str.IsNullOrEmpty,当然,IDE都没有弹出IsNullOrEmpty这个函数我就知道不对了。

  嗯,如果非要写成str.IsNullOrEmpty这样,大概会很麻烦。

  首先string类型就是seal,重写一个string类那就……

  可以写静态方法,但其实也不美观,因为还是要这样 IsNullOrEmpty_test(str), 那和string.IsNullOrEmpty(str);  是没有区别的。

  这个时候,故事终于来到——拓展方法(Extension Methods)。

  下个定义吧,好说话。

  拓展方法,就是把对象自己作为第一个传入参数的静态方法。(这个“对象自己”,需要在形参前加个 this 前缀)

没有规则总是会乱套的,拓展方法有些语法[2]:

l   必须在一个非嵌套的、非泛型的静态类中;

l   至少有一个参数(就是对象它自己);

l   第一个参数必须附加this关键字前缀;

l   第一个参数不能有其他任何修饰符(比如out或ref);

l   第一个参数的类型不能是指针类型。

  那么string.IsNullOrEmpty(str);能怎么改?

    class Program
{
static void Main(string[] args)
{
string str = "";
Console.WriteLine(str.IsNullOrEmpty());
}
} static class NullUtil
{
public static bool IsNullOrEmpty(this string text)
{
return string.IsNullOrEmpty(text);
}
}

  这样看来,可能会有一些疑问,编译器怎么决定要使用的拓展方法?怎么个“非请勿来”?

    首先,如果总是会检测一下是不是实例方法;

    如果不是,就会去查一个合适的拓展方法。它会检查当前的、引用的所有拓展方法。

TIPS:为了决定是否使用一个拓展方法,编译器必须能区分拓展方法与某静态类中恰好具有合适签名的其他方法。为此,它会检查类和方法是否具有System.Runtime.CompilerServices.ExtensionAttribute这个特性(它是.NET3.5新增的)。

  顺带一提,前面提及的InputSimulator类确实这样实现的,思想上是一致的。

    public class KeyboardSimulator : IKeyboardSimulator
{
...
public IKeyboardSimulator KeyPress(VirtualKeyCode keyCode)
{
var inputList = new InputBuilder().AddKeyPress(keyCode).ToArray();
SendSimulatedInput(inputList);
return this;
}
...
}

1.2 Lambda筛选与链接

  LINQ里面.where().Select().OrderBy().等等的“点点点”,就是基于拓展方法的思路。

     var list = new List<string> { "a", "b", "c", "d", "a", "b", "c", "d", "a", "a" };
list = list.Where(a => a.Equals("a")).Reverse().ToList();
list.ForEach(a => Console.WriteLine(a));

  在Where()点击F12,见:

  publicstatic IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

    public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null)
throw Error.ArgumentNull(nameof (source));
if (predicate == null)
throw Error.ArgumentNull(nameof (predicate));
if (source is Enumerable.Iterator<TSource>)
return ((Enumerable.Iterator<TSource>) source).Where(predicate);
if (source is TSource[])
return (IEnumerable<TSource>) new Enumerable.WhereArrayIterator<TSource>((TSource[]) source, predicate);
if (source is List<TSource>)
return (IEnumerable<TSource>) new Enumerable.WhereListIterator<TSource>((List<TSource>) source, predicate);
return (IEnumerable<TSource>) new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);
}

  还有一点注意的是,Where()返回的IEnumerable类型,这是另一个故事。

  这篇写得短一些,主要觉得讲的内容还是保持内容一致性的好,关于LINQ的学习,下一篇继续吧。

注释:

[1] 自 https://archive.codeplex.com/?p=inputsimulator

[2] 自《深入理解C#》(第3版)Jon Skeet 著  姚琪琳 译

【C#进阶】拥抱Lambda(二)的更多相关文章

  1. 【C#进阶】拥抱Lambda(一)

    写在开头,好奇从这里开始(当时让加查询条件,结果竟然是一句话来发挥神奇作用): this.TestGrade = CriteriaHelper.NewObject<ITestCase, DtoT ...

  2. 当我们说线程安全时,到底在说什么——Java进阶系列(二)

    原创文章,同步发自作者个人博客,转载请以超链接形式在文章开头处注明出处http://www.jasongj.com/java/thread_safe/ 多线程编程中的三个核心概念 原子性 这一点,跟数 ...

  3. [.net 面向对象程序设计进阶] (3) 正则表达式 (二) 高级应用

    [.net 面向对象程序设计进阶] (2) 正则表达式 (二)  高级应用 上一节我们说到了C#使用正则表达式的几种方法(Replace,Match,Matches,IsMatch,Split等),还 ...

  4. 网站开发进阶(四十二)巧用clear:both

    网站开发进阶(四十二)巧用clear:both 前言 我们在制作网页中用div+css或者称xhtml+css都会遇到一些很诡异的情况,明明布局正确,但是整个画面却混乱起来了,有时候在IE6下看的很正 ...

  5. Java进阶(三十二) HttpClient使用详解

    Java进阶(三十二) HttpClient使用详解 Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们 ...

  6. Java进阶(五十二)利用LOG4J生成服务日志

    Java进阶(五十二)利用LOG4J生成服务日志 前言 由于论文写作需求,需要进行流程挖掘.前提是需要有真实的事件日志数据.真实的事件日志数据可以用来发现.监控和提升业务流程. 为了获得真实的事件日志 ...

  7. zuul进阶学习(二)

    1. zuul进阶学习(二) 1.1. zuul对接apollo 1.1.1. Netflix Archaius 1.1.2. 定期拉 1.2. zuul生产管理实践 1.2.1. zuul网关参考部 ...

  8. Lambda(二)lambda表达式使用

    Lambda(二)lambda表达式使用 Lambda 表达式组成: /* param list arrow lambda body (o1,o2) -> o1.getColor().Compa ...

  9. c#进阶之lambda表达式

    阅读之前,先确保对委托有基本的了解,传送门 c#进阶之浅析委托和事件. lambda表达式雏形第一步 在委托那篇文章,绑定的的方法都是具名函数,为了简化书写,可以换成匿名函数 public deleg ...

随机推荐

  1. Python编程笔记(第三篇)【补充】三元运算、文件处理、检测文件编码、递归、斐波那契数列、名称空间、作用域、生成器

    一.三元运算 三元运算又称三目运算,是对简单的条件语句的简写,如: 简单条件处理: if 条件成立: val = 1 else: val = 2 改成三元运算 val = 1 if 条件成立 else ...

  2. PHP去除重复的数组数据

    PHP去除重复的数组数据 <?php $input = array("a" => "green","", "red&q ...

  3. 摹客iDoc201901-2新功能点评

    2019才刚刚开始,摹客团队就已经蓄势待发.马不停蹄地给大家带来了又一份惊喜.实话说,这次小摹都忍不住要点个赞!下面就赶紧带大家看看iDoc又更新了哪些新功能: 1.标注和评论融合.协作更高效 iDo ...

  4. vue中常用的指令

    1. v-textv-text主要用来更新textContent,可以等同于JS的text属性. <span v-text="msg"></span> 2. ...

  5. JDK8集合类源码解析 - ArrayList

    ArrayList主要要注意以下几点: 1构造方法 2添加add(E e) 3 获取 get(int index) 4 删除 remove(int index)    ,   remove(Objec ...

  6. Java,JavaScript,jQuery,jSP,js

    js是javascript文件的文件后缀,就像 a.txt 这个.txt是后缀一样 jsp是jsp网页文件的后缀,而jsp是java web 的表现层的一种技术 jquery 是一个函数库,基于jav ...

  7. Vue学习笔记:基础

    Vue实例 每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的 插值 数据绑定最常见的形式就是使用“Mustache”语法(双大括号)的文本插值 指令 指令的定义:Direct ...

  8. 27 isinstance与issubclass、反射、内置方法

    isinstance与issubclass issubclass:判断子类是否属于父类,是则返回True,否则返回False isinstance:判断对象是否属于类,是则返回True,否则返回Fal ...

  9. kbmmw 的HTTPSmartService中的跨域访问

    有同学在使用kbmmw 与extjs 结合的时候,涉及到了跨域访问,这个在 kbmmw 里面已经完全解决. extjs 在访问跨域的时候,首先会使用OPIONS  调用,服务端要根据浏览器请求的 he ...

  10. 2019.01.13 bzoj4538: [Hnoi2016]网络(树链剖分)

    传送门 树链剖分一眼题. 题意简述: 给定一棵树,有三种操作: 加入一条路径 删除一条已加入的路径 询问不过一个点x的路径的最大值. 思路: 直接树链剖分维护答案. 因为询问的事不过点xxx的最大值, ...