如果我使用try / catch / finally块我应该在哪里以及如何初始化变量?例如,假设我正在尝试使用FileStream。我想捕获创建或使用流时抛出的任何异常。然后,无论是否有任何问题,我都希望确保创建的任何流都已关闭。
所以我会做这样的事情:
System.IO.FileStream fs;
try
{
fs = new System.IO.FileStream("C:\test.txt", System.IO.FileMode.Open);
//do something with the file stream
}
catch (Exception exp)
{
//handle exceptions
}
finally
{
//ERROR: "unassigned local variable fs"
if (fs != null)
{
fs.Close();
}
}
然而,这给了我一个错误,在finally块中说unassigned local variable fs
。但是,如果我将fs
的声明更改为System.IO.FileStream fs = null
,则可以。
为什么我需要将fs
显式设置为null?我也试过在try块中声明fs
,但后来我在finally块中得到了错误The name fs does not exsist in the current context
。
BTW:我知道我可以使用Using块,但我的问题是要理解try / catch / finally块的正确用法。
答案 0 :(得分:5)
指定fs = null;
System.IO.FileStream fs = null;
try
{
fs = new System.IO.FileStream("C:\test.txt", System.IO.FileMode.Open);
//do something with the file stream
}
catch (Exception exp)
{
//handle exceptions
}
finally
{
//ERROR: "unassigned local variable fs"
if (fs != null)
{
fs.Close();
}
}
答案 1 :(得分:4)
见规范5.3节。
http://msdn.microsoft.com/en-us/library/aa691172(VS.71).aspx
在可执行文件中的给定位置 函数成员的代码,变量 据说是肯定的 编译器可以通过静态流程证明 分析,变量已经 自动初始化或已经过 至少一项任务的目标。
使用try / catch / finally时,当您尝试访问try
块中的对象时,无法保证finally
块的分配。如您所见,您可以通过为变量指定初始值来满足要求(在本例中为null
)。
答案 2 :(得分:3)
C#设计团队认为确保明确初始化事物是一个好主意。我倾向于同意;在C ++中,我已经被来自未初始化变量的错误所困扰。
答案 3 :(得分:2)
将fs
初始化为null
是正确的用法。编译器希望确保您只读取初始化变量,以避免严重错误。并且它无法保证您的try块中的初始化被执行
答案 4 :(得分:1)
我的纯粹主义者想要做这样的事情:
void DoSomethingWithStream()
{
try
{
System.IO.FileStream fs = new System.IO.FileStream(@"C:\test.txt", System.IO.FileMode.Open);
try
{
// do something with the file stream
}
catch (Exception ex)
{
// handle exceptions caused by reading the stream,
// if these need to be handled separately from exceptions caused by opening the stream
}
finally
{
// FileStream.Close might throw an exception, so put FileStream.Dispose in a separate try/finally
fs.Dispose();
}
}
catch (Exception ex)
{
// handle exceptions that were either thrown by opening the filestream, thrown by closing the filestream, or not caught by the inner try/catch
}
}
尽管如此,这将是一个混乱:
void DoSomethingWithStream_PartDeux()
{
try
{
System.IO.FileStream fs = new System.IO.FileStream(@"C:\test.txt", System.IO.FileMode.Open);
try
{
try
{
// do something with the file stream
}
catch (Exception ex)
{
// handle exceptions caused by reading the stream,
// if these need to be handled separately from exceptions caused by opening the stream
}
finally
{
fs.Close();
}
}
finally
{
// FileStream.Close might throw an exception, so put FileStream.Dispose in a separate try/finally
fs.Dispose();
}
}
catch (Exception ex)
{
// handle exceptions
}
}
访问数据库可能更糟糕:
void DoSomethingWithDatabase()
{
var connection = new System.Data.SqlClient.SqlConnection("Connect to mah database!");
try
{
var command = new System.Data.SqlClient.SqlCommand("Get mah data!", connection);
connection.Open();
try
{
var reader = command.ExecuteReader();
try
{
try
{
// read data from data reader (duh)
}
finally
{
reader.Close();
}
}
finally
{
reader.Dispose();
}
}
finally
{
connection.Close();
}
}
finally
{
connection.Dispose();
}
}
但是,在大多数情况下,如果您要在之后立即处理它们(除非您真的是偏执狂),我真的不需要明确地关闭您的流/连接/数据读取器。因此,上面的数据库代码可以很容易就是这样:
void DoSomethingWithDatabase_PartDeux()
{
using (var connection = new System.Data.SqlClient.SqlConnection("Connect to mah database!"))
{
var command = new System.Data.SqlClient.SqlCommand("Get mah data!", connection);
connection.Open();
using(var reader = command.ExecuteReader())
{
// read data from data reader (duh)
}
}
}
也许我刚被Wily博士的邪恶API编码所玷污。使用initialize-variable-to-null技巧不适用于他的框架:
void DoSomethingWithDrWilyEvilBoobyTrap()
{
Dr.Wily.Evil.BoobyTrap trap = null;
try
{
trap = new Dr.Wily.Evil.BoobyTrap(Dr.Wily.Evil.Evilness.Very);
// do something with booby trap
}
catch (Exception ex)
{
// handle exceptions
}
finally
{
if (trap != null) // Exception thrown here!
trap.Dispose(); // Exception thrown here as well!
}
}
以下是他的API中的一些源代码的预览:
public enum Evilness
{
Slight,
Moderate,
Very,
}
class BoobyTrap : IDisposable
{
public Evilness Evil { get; protected set; }
public BoobyTrap(Evilness evil)
{
this.Evil = evil;
}
public void DoEvil()
{
// ... snip (sorry, it's just too evil) ...
}
public static bool IsNull(BoobyTrap instance)
{
throw new Exception("I bet you thought this function would work, didn't you? Well it doesn't! You should know whether or not your variables are null. Quit asking me!");
}
public static bool operator !=(BoobyTrap x, object y)
{
if(y == null)
throw new Exception("You cannot check if an instance of a BoobyTrap is null using the != operator. Mwahahaha!!!");
return x.Equals(y);
}
public static bool operator ==(BoobyTrap x, object y)
{
if (y == null)
throw new Exception("You cannot check if an instance of a BoobyTrap is null using the == operator. Mwahahaha!!!");
return x.Equals(y);
}
#region IDisposable Members
public void Dispose()
{
switch (this.Evil)
{
case Evilness.Moderate:
case Evilness.Very:
throw new Exception("This object is cursed. You may not dispose of it.");
}
}
#endregion
}
答案 5 :(得分:0)
你很亲密。我将声明设置为null
。
System.IO.FileStream fs = null;
try
{
fs = new System.IO.FileStream("C:\test.txt", System.IO.FileMode.Open);
//do something with the file stream
}
catch (Exception exp)
{
//handle exceptions
}
finally
{
//ERROR: "unassigned local variable fs"
if (fs != null)
{
fs.Close();
}
}
我认为当你不能使用using
声明时,这是非常可以接受的。