《Effective C#》读书笔记——条目8:推荐使用查询语法而不是循环<C#语言习惯>
2012-09-01 21:19
591 查看
查询语法(query syntax)可以让程序逻辑的表达由“命令式”转换为“声明式”。查询语法定义了想要的结果,而把具体实现交给其他的专门实现。使用查询语法(实现了查询表达式模式的方法语法也可以)要比传统的命令式循环结果更加清晰的表达你的意图。
下面我们观察一个使用命令式方法填充一个数组,然后将其内容输出至控制台:
编写命令式的代码需要关注具体的实现细节。但是如果采用查询语法,实现同样的功能,代码更加易于重用且易读,我们来看上面示例“声明式”的写法:
我们看到在负责循环打印部分我们使用了一个扩展方法,这个扩展方法带来了更好的重用性,每次需要对一个序列的元素执行某个操作都可以使用ForAll()方法:
上面的实例比较简单,似乎看不出二者有多大的区别,在下面的实例中我们分别使用“命令式”和“声明式”来实现比较二者的区别 。
命令式:
View Code
我们可以看到随着编程任务的复杂:
“命令式”版本变得越来越难以理解。如果仔细看的话,甚至都不会发现比较函数中参数被颠倒了(这是个错误),而这只是为了能够降序排列而已。要是没有任何注释和稳定,命令式代码将会更加难以阅读。”命令式“代码太过于强调实现目标所需要的详细步骤,以至于让人很容易陷入具体的细节中。
“声明式”版本的最后一个实现,实际只是将以此过滤(where子句)、以此排序(orderby子句)和一个投射(select)组合起来。查询语法比循环结构能够提供更具组合性的API。查询语法很自然的将算法分解成小块代码,每一块代码静对序列中的元素进行单一操作。查询语法的延迟执行模式也让开发者能够将这些单一的操作组合成多步的操作,且只要一次遍历序列就可以完整执行,而循环语法结构则必须为每一步操作都创建临时的存储,或者为序列将要执行的每一批操作都创建专用的方法。
小节:
当你需要编写循环时,首先看看能否用查询语法实现,若是无法使用查询语法,那么再看看是否能以方法调用语法替代。这样写出的代码总会比命令式循环结构要简洁一些。
下面我们观察一个使用命令式方法填充一个数组,然后将其内容输出至控制台:
static void Main(string[] args) { int[] foo = new int[100]; for (int num = 0; num < foo.Length; num++) { foo[num] = num * num; } foreach (int i in foo) { Console.WriteLine(i); } Console.Read(); }
编写命令式的代码需要关注具体的实现细节。但是如果采用查询语法,实现同样的功能,代码更加易于重用且易读,我们来看上面示例“声明式”的写法:
static void Main(string[] args) { //生成数组的工作交个一个查询完成 int[] foo = (from n in Enumerable.Range(0, 100) select n * n).ToArray(); //循环打印的工作交给一个数组的扩展方法来完成 foo.ForAll((n) => Console.WriteLine(n.ToString())); Console.Read(); }
我们看到在负责循环打印部分我们使用了一个扩展方法,这个扩展方法带来了更好的重用性,每次需要对一个序列的元素执行某个操作都可以使用ForAll()方法:
public static class Extensions { /// <summary> /// 为IEnumerable<T>类型添加扩展方法 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="sequence"></param> /// <param name="action"></param> public static void ForAll<T>(this IEnumerable<T> sequence, Action<T> action) { foreach (T item in sequence) { action(item); } } }
上面的实例比较简单,似乎看不出二者有多大的区别,在下面的实例中我们分别使用“命令式”和“声明式”来实现比较二者的区别 。
命令式:
View Code
private static IEnumerable<Tuple<int, int>> QueryIndices() { #region 用0到99的整数生产所以的(X,Y)二元组 //return from x in Enumerable.Range(0, 100) // from y in Enumerable.Range(0, 100) // select Tuple.Create(x, y); #endregion #region X和Y的和要小于100 //return from x in Enumerable.Range(0, 100) // from y in Enumerable.Range(0, 100) // where x + y < 100 // select Tuple.Create(x, y); #endregion #region 二元组按照其离远点的距离逆序排列 return from x in Enumerable.Range(0, 100) from y in Enumerable.Range(0, 100) where x + y < 100 orderby (x * x + y * y) descending select Tuple.Create(x, y); #endregion }
我们可以看到随着编程任务的复杂:
“命令式”版本变得越来越难以理解。如果仔细看的话,甚至都不会发现比较函数中参数被颠倒了(这是个错误),而这只是为了能够降序排列而已。要是没有任何注释和稳定,命令式代码将会更加难以阅读。”命令式“代码太过于强调实现目标所需要的详细步骤,以至于让人很容易陷入具体的细节中。
“声明式”版本的最后一个实现,实际只是将以此过滤(where子句)、以此排序(orderby子句)和一个投射(select)组合起来。查询语法比循环结构能够提供更具组合性的API。查询语法很自然的将算法分解成小块代码,每一块代码静对序列中的元素进行单一操作。查询语法的延迟执行模式也让开发者能够将这些单一的操作组合成多步的操作,且只要一次遍历序列就可以完整执行,而循环语法结构则必须为每一步操作都创建临时的存储,或者为序列将要执行的每一批操作都创建专用的方法。
小节:
当你需要编写循环时,首先看看能否用查询语法实现,若是无法使用查询语法,那么再看看是否能以方法调用语法替代。这样写出的代码总会比命令式循环结构要简洁一些。
相关文章推荐
- 《Effective C#》读书笔记——条目3:推荐使用is或as而不是强制转换类型<C#语言习惯>
- 《Effective C#》读书笔记——条目4:使用Conditional特性而不是#if条件编译<C#语言习惯>
- 《Effective C#》读书笔记——条目1:使用属性而不是可访问的数据成员<C#语言习惯>
- C#语言习惯 - 推荐使用查询语法而不是循环
- 《Effective C#》读书笔记——条目2:用运行时常量而不是编译期常量<C#语言习惯>
- 《Effective C#》读书笔记——条目10:使用可选参数减少方法重载的数量<C#语言习惯>
- 《Effective C#》读书笔记——条目5:为类型提供ToString()方法<C#语言习惯>
- 《Effective C#》读书笔记——条目6:理解几个等同性判断之间的关系<C#语言习惯>
- 《Effective C#》读书笔记——条目11:理解短小方法的优势<C#语言习惯>
- 《Effective C#》读书笔记——条目22:通过定义并实现接口替代继承<使用C#表达设计>
- 《Effective C#》读书笔记——条目24:用委托实现回调<使用C#表达设计>
- 《Effective C#》读书笔记——条目28:提供粗粒度的互联网API<使用C#表达设计>
- 《Effective C#》读书笔记——条目25:用事件模式实现通知<使用C#表达设计>
- 《Effective C#》读书笔记——条目23:理解接口方法和虚方法的区别<使用C#表达设计>
- 《Effective C#》读书笔记——条目27:让类型支持序列化<使用C#表达设计>
- 《Effective C#》读书笔记——条目26:避免返回对内部类对象的引用<使用C#表达设计>
- 《Effective C#》读书笔记——条目21:限制类型的可见性<使用C#表达设计>
- 《Effective C#》读书笔记——条目12:推荐使用成员初始化器而不是赋值语句<.NET资源管理>
- 改善 C# 的语言习惯(一) - 使用属性而不是可访问的数据成员(整理中)
- 在.NET Core中使用Irony实现自己的查询语言语法解析器