用 LINQ 编写 C# 都有哪些一招必杀的技巧?

来自南京的.NET码农,微软MVP,CSDN版主。将近10年里干过架构师,也当过产品经理,csdn中的caozhy,人称老曹。程序员中英语不错,业余还玩玩天文,猫奴一枚。

文章正文

前言

感谢大家选择我的这个主题。

C# 作为一种全能的编程语言,以及微软近年来的转型,越来越受到开发者的重视。C# 不但可以用来开发基于 Linux 和 Docker 上运行的轻量的 Web 应用,也可以在 Xamarin 的加持下开发 Android 和 iOS 移动客户端程序,C#+Unity3D 近年来作为热门的技术被很多网游公司作为首选的方案。别忘记了,C# 还是目前开发 Windows 桌面程序的第一方案,随着 C# 应用领域的扩展,其逐步变得热门起来,所以现在越来越多的程序员开始关注这一语言。因为 C# 的开发领域如此广阔,所以学好这一语言的投入产出越来越高。

很多新学 C# 的开发者有过学习 C++ 和 Java 以及 JavaScript 的基础,毕竟大部分的学校都会教授 C++ 和 Java 课程,因此和 C++/Java 语法类似的 C# 上手起来难度不是很高。然而 LINQ 语法是一个例外,一般程序员看到有 LINQ 代码的 C# 程序会感觉理解起来很吃力。一部分 C# 程序员也认为,LINQ 是 SQL 的一种替代产物,他们只有在数据查询的时候才会用 LINQ,而且仅仅用 LINQ 来做原先他们用 SQL 做的事情。

然而,LINQ 实际上是 C# 语言语法的一等公民,LINQ 其实一点也不神秘,本文将首先从机制上让你对 LINQ 有个清晰的了解,然后会给出 LINQ 在日常编程中的一些用法,限于篇幅,本文的内容是启发性的,目的是让你知道 LINQ 的原理以及 LINQ 还可以这么用。至于一些细节,则还需要你额外地学习。

俗话说,纸上得来终觉浅,为了避免纸上谈兵,本文不可避免地会列出很多代码,理解这些代码的最好方式是自己动手尝试下。然而,也许你的电脑上还暂时没有开发环境,虽然安装开发环境不是难事,但是总归会成为一些人动手尝试的障碍。为了方便你立刻动手进行探索,首先推荐一个网站,叫做 ideone.com,顾名思义,它提供了一个在线的编辑、编译调试环境,只要贴上代码,并且提供输入的行,它就能在线编译执行并且返回结果。这个网站支持很多编程语言,C# 也支持,需要指出,它的编译器是基于 mono 开发的,和最新版本的 C# 编译器略有不同,一些新的语法可能不支持。另外,这个网站也并不能在线运行 Winforms 或者 ASP.NET Web 程序,所以它并不能完全代替你在自己的 PC 上练习和开发程序。

在浏览器里输入 ideone.com,打开网站,可以看到,默认下编辑语言是 Java,点 Java 下拉箭头,出现一个语言选择的菜单,可以选择 C#。如图所示。

enter image description here

可以立刻输入一个简单的程序,比如:

Console.WriteLine(“hello world”);

然后点绿色的 Submit 按钮,此时稍等片刻,程序就编译执行了,我们可以看到结果出现在 stdout 里,如图:

enter image description here

本文所列出的代码,会给出 ideone 的链接,这样你不需要做任何事情,就能直接看到代码和结果。然而,也许你想更改下输入,看看结果有什么不同,或者更改下程序。此时你只要点 fork,就能产生一个新的代码片段,从而可以修改了。

如本程序我放在了 https://ideone.com/juq1Eg 下,你点 fork 会出现一个新的编辑页面,此时你点下面的 stdin 按钮,可以添加输入。比如如图:

enter image description here

然后点 run 按钮,就能看到结果:

hello zhangsan

LINQ 的本质

首先要说明一点,本文讨论的 LINQ,如果没有特指,说的是 LINQ To Object,这种方式使用 System.Linq 命名空间的本地代码实现查询,虽然语法上和 LINQ To EF 类似,但是后者是通过表达式树翻译成 SQL 查询,在数据库端执行的。

LINQ To Object(以下简称 LINQ)的操作符,并非是在语法层面定义和实现,而是通过类库实现的,这意味着 LINQ 的查询,和调用一般的 C# 代码没有什么区别。LINQ 有两种写法,LINQ 表达式写法和调用 LINQ 操作符(查询方法)。比如如下两个简单的查询,实现了相同的功能,将一个数组中的偶数挑出来:

使用 LINQ 表达式:

int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8 };
var query = from x in arr where x % 2 == 0 select x;
foreach (int x in query) Console.Write(x + "\t");

使用 LINQ 操作符:

int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8 };
var query = arr.Where(x => x % 2 == 0);
foreach (int x in query) Console.Write(x + "\t");

如果你使用 ideone 来编写如上的代码,请记得在开头加上:

using System.Linq;

以上两段代码是等价的。初看这两段代码,让人觉得很困惑。原因是,第一段代码看上去像 SQL 查询,但是写法却和 SQL 略有不同,给人的印象是,似乎 C# 幕后有个类似数据库的东西在查询,感觉很“玄”。第二段代码,似乎“正常”一些,然而 Where() 这个方法里的 Lambda 表达式却让人看不明白,这个x是什么,好像没看到在哪里定义啊。

我想,很多人学了 LINQ,并且从入门到放弃的根本原因就在这里。他们对 Lambda 表达式不理解,所以不太会用第二种办法,他们会结合 SQL 的经验和举一反三地使用第一种方法,但是对 LINQ 的运行机制不太了解,而遇到复杂的查询就晕了。

为了揭示 LINQ 的本质,我们抛开 LINQ,自己先实现一个LINQ。自己实现LINQ?听上去会很复杂?然而其实很简单,只要一点点代码。

还记得前面我说的,使用ideone来编写如上的代码,请记得在开头加上:

using System.Linq;

那么,如果不加上它会如何呢?你可以试试看。

enter image description here

看到了么?提示缺少Where方法。显然Where方法是在System.Linq这个命名空间中定义的。你可能会奇怪,我们写的代码中并没有调用Where方法啊?但是看了我前面的介绍你应该知道,C#编译器会自动将LINQ表达式转换为对LINQ操作符的调用。所以你的代码实际上相当于:

var query = arr.Where(x => x % 2 == 0);

而在这段代码里,我们其实调用了Where方法。现在你可能会想,如果我们自己写一个Where方法,结果会如何呢?我们来试试看:

using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    static class MyLinq
    {
        public static IEnumerable<int> Where(this int[] arr, Func<int, bool> cond)
        {
            return new int[] { 2, 4, 6, 8 };
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8 };
            var query = from x in arr where x % 2 == 0 select x;
            foreach (int x in query) Console.Write(x + "\t");
        }
    }
}

或者参见:https://ideone.com/MMM5y7

尝试运行下,结果是:

2    4   6   8   

细心的读者看到int[] arr前面有个this,这说明这是一个扩展方法,扩展方法允许将静态方法模拟成第一个参数所代表的对象的成员方法。关于扩展方法的有关内容不是本文的讨论范围。有需要了解的可以参考这篇文章 看,我们实现了一个最基本的LINQ。这代码看上去太简单了吧,看着让人怀疑啊,你肯定想试试看,把arr修改为{1,2,3,4,5,6,7,8,9,10}。一试,果然露馅了。结果还是2 4 6 8,没有10。这很好理解,因为我们是硬编码返回的2,4,6,8,并没有将数组传入,也没有将条件传入,自然结果是写死的。这样的“LINQ”自然没用。另外Func<int, bool> cond这是什么鬼,看着又奇怪了。

别着急,这个叫做委托。所谓委托,就是用它表示一个函数,这个函数从调用者看,可以是任意的函数名,甚至没有函数名,从被调用者看,它叫做cond,我们直接调用它就可以了。后面我们再详细说,看下面的代码:


                    
作者正在撰写中...
隐藏内容 支付可见
内容互动
写评论
加载更多
评论文章
¥4.99 购买
× 订阅 Java 精选频道
¥ 元/月
订阅即可免费阅读所有精选内容