扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
这个版本不得不引入一个try程序块(既然一个finally程序快必须跟随在一个try程序块后),这将是一个合理的解决方案,如果它奏效的话。但是,它没有做到。问题是try程序块构建成一个范围,所以在finally程序块中的reader并不在这个范围内并且返回语句中的source也不在这个范围。
finally?
为了解决这个问题,你不得不将reader和source的声明移到try程序块的外面,第二次尝试如下:
private static char[] ReadSource(string filename)
{
TextReader reader;
char[] source;
try
{
FileInfo file = new FileInfo(filename);
int length = (int)file.Length;
source = new char[length];
reader = file.OpenText();
reader.Read(source, 0, length);
}
finally
{
reader.Close();
}
return source;
}
这个版本将reader和source的声明移到了try程序块的外面,接着指派给reader和source但没有初始化它们。这是和开始的“理想”版本不同的另外一个地方(出现两个多余行)。然而,你可能认为如果它工作,那将是一个合理的解决方案。但是它没有。问题是委派并不等同于初始化及让编译器知道它。如果在reader被分配之前出现一个异常,这是在finally程序块中对reader.close()的调用
将根据没有被分配的reader,C#,像Java一样,不允许那样。
finally?
很明显,你必须要初始化reader,第三次尝试如下:
private static char[] ReadSource(string filename)
{
TextReader reader = null;
char[] source;
try
{
FileInfo file = new FileInfo(filename);
int length = (int)file.Length;
source = new char[length];
reader = file.OpenText();
reader.Read(source, 0, length);
}
finally
{
reader.Close();
}
return source;
}
这个版本引入了空值,这没有出现在最初的“理想版本”中。不过,如果你仍然认为如果它起作用这将是一个合理的解决方式,然而它不是(虽然它能通过编译)。问题是你在调用reader.close()的时候很容易抛出NullReferenceException异常。
finally?
一种解决方法是对eader.close()方法进行保护,下面做第四次尝试:
private static char[] ReadSource(string filename)
{
TextReader reader = null;
char[] source;
try
{
FileInfo file = new FileInfo(filename);
int length = (int)file.Length;
source = new char[length];
reader = file.OpenText();
reader.Read(source, 0, length);
}
finally
{
if (reader != null)
{
reader.Close();
}
}
return source;
}
当然,对reader.close()的保护并不是ReadSource的理想版本。但是,如果仅从它的效果上看,这将是一个合理的版本。最终,工作。它和最初的版本已经大不相同。稍微努力,你能复用下面这段代码:
private static char[] ReadSource(string filename)
{
FileInfo file = new FileInfo(filename);
int length = (int)file.Length;
char[] source = new char[length];
TextReader reader = file.OpenText();
try
{
reader.Read(source, 0, length);
}
finally
{
if (reader != null)
{
reader.Close();
}
}
return source;
}
在某些情况下,你可以在finally程序块中对可能出现空值进行判断(上面就是一个这样的例子),但是一般说来,你最好还是在finally程序块中判断一下(考虑到如果file.OpenText返回空值,或者如果reader被放进了try程序块中,或者reader作为ref/out参数被传递到try程序块中)。你不得不增加一个try程序块,一个finally程序块,和一个if防护。如果你正在使用Java,你不得不每一次都去做这些事情。在那里是个最大的问题。如果这个解决方案非常令人厌烦且完全偏离于最初的“完美”方案,这没有关系,你能够将这些异常提取到一块。在Java中,你不能这么做。遇到异常,Java将停止运行,而C#则将继续。
四、using语句
在C#中,最接近于“理想”版本的是使用using语句:
private static char[] ReadSource(string filename)
{
FileInfo file = new FileInfo(filename);
int length = (int)file.Length;
char[] source = new char[length];
using (TextReader reader = file.OpenText())
{
reader.Read(source, 0, length);
}
return source;
}
Reader将会被恰当的关闭。简单说来,using语句有大量的特征能够改善开始的“理想”版本。首先,我们看一下它内在的运行机制到底是怎样的。
using语句转换
C#ECMA标准描述using声明:
using (type variable = initialization)
embeddedStatement
它等同于
{
type variable = initialization;
try
{
embeddedStatement
}
finally
{
if (variable != null)
{
((IDisposable)variable).Dispose();
}
}
}
它依赖于System命名空间中的IDisposable接口:
namespace System
{
public interface IDisposable
{
void Dispose();
}
}
注意:finally程序块中的牵制转换意味着,这个变量必须是一个支持IDisposable接口的类(通过继承或转换操作)。如果它不是,你就会得到一个编译时错误。
using TextReader 转换
不出乎意料,TextReader支持IDisposable接口,并且实现了Dispose来调用关闭。这意味着:
using (TextReader reader = file.OpenText())
{
reader.Read(source, 0, length);
}
相当于下面:
{
TextReader reader = file.OpenText();
try
{
reader.Read(source, 0, length);
}
finally
{
if (reader != null)
{
((IDisposable)reader).Dispose();
}
}
}
除了对IDisposable的强制转换外,这和最通用的Java解决方式是相同的。这个强制转换是必须的因为这是一个通用解决方式。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者