浅谈 C# 语言的 using 语句

背景知识

外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。外部排序最常用的算法是多路归并排序,即将原文件分解成多个能够一次性装入内存的部分,分别把每一部分调入内存完成排序。然后,对已经排序的子文件进行归并排序。

问题提出

假设我们要写一个外部排序程序。现在要讨论的是对已经排序的子文件进行归并排序。

解决方案1

下面是外部排序归并阶段的代码片段:

01:  class ExternalSorting
02:  {
03:    void Merge(string inputFileName1, string inputFileName2, string outputFileName)
04:    {
05:      using (var reader1 = new StreamReader(inputFileName1))
06:      {
07:        using (var reader2 = new StreamReader(inputFileName2))
08:        {
09:          using (var writer = new StreamWriter(outputFileName))
10:          {
11:            Merge(reader1, reader2, writer);
12:          }
13:        }
14:      }
15:    }
16:
17:    void Merge(TextReader reader1, TextReader reader2, TextWriter writer)
18:    {
19:      var s1 = reader1.ReadLine();
20:      var s2 = reader2.ReadLine();
21:      while (s1 != null || s2 != null)
22:      {
23:        if (Compare(s1, s2) <= 0) StepIt(ref s1, reader1, writer);
24:        else StepIt(ref s2, reader2, writer);
25:      }
26:    }
27:
28:    int Compare(string s1, string s2)
29:    {
30:      if (s1 == null && s2 == null) throw new ArgumentException("s1 和 s2 不能同时为 null");
31:      if (s1 == null) return 1;
32:      if (s2 == null) return -1;
33:      return string.Compare(s1, s2);
34:    }
35:
36:    void StepIt(ref string s, TextReader reader, TextWriter writer)
37:    {
38:      writer.WriteLine(s);
39:      s = reader.ReadLine();
40:    }
41:  }

上述代码中的第 05 到 14 行的三个 using 语句逐个嵌套,依次缩进,是不是很难看?

注意,上述代码中第 33 行可以替换为你想要的比较大小的方法,以便按照不同的关键字进行排序。

解决方案2

我们知道,可以将多个对象与 using 语句一起使用,但必须在 using 语句中声明这些对象。因此,我们可以将上述的第 05 到 14 行的代码重构如下:

1:  using (TextReader reader1 = new StreamReader(inputFileName1),
2:                    reader2 = new StreamReader(inputFileName2))
3:  {
4:    using (TextWriter writer = new StreamWriter(outputFileName))
5:    {
6:      Merge(reader1, reader2, writer);
7:    }
8:  }

但是还是有两个嵌套的 using 语句,不爽。

解决方案3

我们还知道,C# 编译器实际上将 using 语句转化为 try - finally 块。那么我们继续进行重构:

01:  TextReader reader1 = null;
02:  TextReader reader2 = null;
03:  TextWriter writer = null;
04:  try
05:  {
06:    reader1 = new StreamReader(inputFileName1);
07:    reader2 = new StreamReader(inputFileName2);
08:    writer = new StreamWriter(outputFileName);
09:    Merge(reader1, reader2, writer);
10:  }
11:  finally
12:  {
13:    if (reader1 != null) reader1.Dispose();
14:    if (reader2 != null) reader2.Dispose();
15:    if (writer != null) writer.Dispose();
16:  }

这样看起来很不错的样子。注意:

  • 如果上述代码片段不是 Merge 方法中仅有的代码块,请使用一对大括号将其括起来,以便为上述三个对象(reader1、reader2 和 writer)创建有限的范围。
  • 实际上 C# 编译器对解决方案1和解决方案2生成的 IL 代码是差不多的,都是三个嵌套的 try - finally 块。而不是象解决方案3中那样只有一个 try - finally 块。

解决方案4

我们知道 using 语句只不过是提供能确保正确使用 IDisposable 对象的方便语法。using 语句按照正确的方式调用对象上的 Dispose 方法,并会导致在调用 Dispose 时对象自身处于范围之外。因此,可以重构如下:

1:  using (IDisposable reader1 = new StreamReader(inputFileName1),
2:                     reader2 = new StreamReader(inputFileName2),
3:                     writer = new StreamWriter(outputFileName))
4:  {
5:    Merge(reader1 as TextReader, reader2 as TextReader, writer as TextWriter);
6:  }

这是我最喜欢的方案,你们呢?

解决方案5

但是,解决方案4中第 5 行中使用三个 as 进行强制类型转换毕竟不爽。考虑进行以下重构:

1:  using (TextReader reader1 = new StreamReader(inputFileName1),
2:         TextReader reader2 = new StreamReader(inputFileName2),
3:         TextWriter writer = new StreamWriter(outputFileName))
4:  {
5:    Merge(reader1, reader2, writer);
6:  }

可惜,上述代码片段无法通过编译,出现以下编译错误:

CS1044: 在 for、using 和 fixed 或声明语句中不能使用多个类型。

这个编译错误实在是没有道理。实际上在 for、using 和 fixed 语句中能够使用多个类型对程序员来说很有用的,而且 C# 编译器实现这点也没有什么困难。你们以为呢?

解决方案6

好吧,我们换一种方法进行重构:

1:  using (var reader1 = new StreamReader(inputFileName1),
2:             reader2 = new StreamReader(inputFileName2),
3:             writer = new StreamWriter(outputFileName))
4:  {
5:    Merge(reader1, reader2, writer);
6:  }

非常遗憾,还是无法通过编译。这次的错误信息如下:

CS0819: 隐式类型的局部变量不能有多个声明符。

这个编译错误也是没有道理的。同样,在隐式类型的局部变量中有多个声明符对程序员来说也是很有用的,对 C# 编译器来说也没有什么实现上困难。你们以为呢?

结论

建议修改 C# 编译器,去掉 CS1044 和 CS0819 编译错误,以便允许解决方案5和解决方案6,造福广大 C# 程序员。

对于目前的 C# 编译器,建议使用解决方案4。

版权声明:本文为博主http://www.zuiniusn.com原创文章,未经博主允许不得转载。

时间: 2024-08-15 01:29:35

浅谈 C# 语言的 using 语句的相关文章

浅谈C语言中的联合体(转载)

联合体union 当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union).在C Programming Language 一书中对于联合体是这么描述的: 1)联合体是一个结构: 2)它的所有成员相对于基地址的偏移量都为0: 3)此结构空间要大到足够容纳最"宽"的成员: 4)其对齐方式要适合其中所有的成员: 下面解释这四条描述: 由于联合体中的所有成员是共享一段内存的,因此每个成员的存放首地址相对于于联合体变量的基地址的偏移量为0,即所有成员的首地址都是一样的.为

浅谈C语言中的强符号、弱符号、强引用和弱引用

摘自http://www.jb51.net/article/56924.htm 浅谈C语言中的强符号.弱符号.强引用和弱引用 投稿:hebedich 字体:[增加 减小] 类型:转载 时间:2014-10-31 我要评论 这篇文章主要介绍了C语言中的强符号.弱符号.强引用和弱引用的定义及相关内容,非常的简单易懂,有需要的朋友可以参考下 首先我表示很悲剧,在看<程序员的自我修养--链接.装载与库>之前我竟不知道C有强符号.弱符号.强引用和弱引用.在看到3.5.5节弱符号和强符号时,我感觉有些困惑

浅谈c语言typedef 与结构体指针(个人小经验)

 #include<stdio.h> #include<string.h> typedef struct emp{ char sex[8]; char name[15]; int age; }*emp;//这里我们用typedef把emp这个结构体变成了*emp这种指向结构体成员的结构体指针 /*typedef struct emp{ char sex[8]; char name[15]; int age; }pi,*emp;//为了程序的可读性最好不要这样声明*/ int m

浅谈C语言字符串结束符&#39;\0&#39;

如果你希望你的字符串以’\0‘结束,那么你可以这样做: 1 char str[]={"hello"};//①字符串赋值 2 char str[]={'h','e','l','l','o','\0'};//②人为添加 3 char str[6]={'h','e','l','l','o'};//③故意给数组预留一个空位 注:当出现以下情况时,会发生'\0'丢失 1 char str[5]={"hello"};//①数组长度不够 2 char str[]={'h','e'

浅谈C语言内存管理、内存泄露、堆栈

1.内存分配区间: 对于一个C语言程序而言,内存空间主要由五个部分组成:代码段(.text).数据段(.data).静态区(.BSS).堆和栈组成. BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量和静态变量 (这里注意一个问题:一般的书上都会说全局变量和静态变量是会自动初始化的,那么哪来的未初始化的变量呢?变量的初始化可以分为显示初始化和隐式初始化,全局变量和静态变量如果程序员自己不初始化的话的确也会被初始化,那就是不管什么类型都初始化为0,这种没有显示初始

浅谈C语言嵌入式系统编程注意事项

C语言嵌入式系统编程注意事项之背景篇 本文的讨论主要围绕以通用处理器为中心的协议处理模块进行,因为它更多地牵涉到具体的C语言编程技巧 不同于一般形式的软件编程,嵌入式系统编程建立在特定的硬件平台上,势必要求其编程语言具备较强的硬件直接操作能力.无疑,汇编语言具备这样的特质.但是,归因于汇编语言开发过程的复杂性,它并不是嵌入式系统开发的一般选择.而与之相比,C语言--一种"高级的低级"语言,则成为嵌入式系统开发的最佳选择.笔者在嵌入式系统项目的开发过程中,一次又一次感受到C语言的精妙,沉

浅谈 C 语言中模块化设计的范式

今天继续谈模块化的问题.这个想慢慢写成个系列,但是不一定连续写.基本是想起来了,就整理点思路出来.主要还是为以后集中整理做点铺垫. 我们都知道,层次分明的代码最容易维护.你可以轻易的换掉某个层次上的某个模块,而不用担心对整个系统造成很大的副作用. 层次不清的设计中,最糟糕的一种是模块循环依赖.即,分不清两个模块谁在上,谁在下.这个时候,最容易牵扯不清,其结果往往是把两者看做一体去维护算了.这里面还涉及一些初始化次序等繁杂的细节. 其次,就是越层的模块联系.当模块 A 是模块 B 的上层,而模块

编程之美,让美国人科技高速发展,浅谈C语言带给美国的变化

我去年7月份有幸应美国朋友的邀约,在美国众多正在飞速发展中的高科技型企业畅游了一番.本来我以为,美国只有Google公司,苹果公司,FaceBook,IBM,微软,思科这些巨型的高新技术企业在世界的新技术领域活跃.然而真正的到了美国,我才真正的发现美国远远比我想象的更加丰富多彩.而让美国高新科技产业展现蓬勃生机的因素,我深深的发现C语言确实功不可没. C语言是一门通用计算机编程语言,广泛应用于底层开发.C语言的设计目标是以一种简易的方式编译.处理低级存储器,产生少量的机器码以及不需要任何的运行环

浅谈c语言程序为什么需要内存 栈又是什么?

1 1.关于内存(程序的执行需要内存的支持) 2 (1)内存本身在物理上是硬件器件,由操作系统提供 3 (2)内存的管理最终由操作系统统一管理.为了能过便捷的管理内存(酒店管理房间 是不是分很多不同的类型和待遇呢),同样操作系统提供了多种的机制来让了多种机制来让我们应用程序使用内存.这些机制彼此不同,各自有各自的特点,我们程序根据自己的实际情况来选择某种方式获取内存(在操作系统处登记这块内存的临时使用权限).使用内存.释放内存(向操作系统归还这块内存的使用权限).也就是在进入酒店拿到钥匙或者卡,