看看下面的代码:
private void button1_Click(object sender, EventArgs e){
using (SqlConnection conn = new SqlConnection("Data Source=.; Initial Catalog=TestDb; Integrated Security=SSPI;"))
{
conn.Disposed += Conn_Disposed;
conn.Open();
decimal m = 1;
decimal x = 3 / (m - 1); // 出错
conn.Close();
}
}
private void Conn_Disposed(object sender, EventArgs e)
{
MessageBox.Show("conn disposed"); // 这里仍然执行了
}
故意放一句出错的代码――除以 0。
由于没有加 try、catch,所以肯定会抛出异常,但是由于我们使用了 using,C#在抛出异常前,会先把 Conn_Disposed执行了,然后再抛出异常。
顺便注意一下:
由于抛出异常,所以 conn.Close()这句并没有被调用,不过不用担心,using自动执行 conn.Dispose(),Dispose()会调用 Close()。 若没有抛出异常,conn.Close()与 conn.Dispose()将都被执行,这并不会出错。 conn.Close()在这里的确可以省略,我写出来大概是出于我的习惯吧。using为我们带来了简便的写法,否则我们可能需要这么写:
private void button1_Click(object sender, EventArgs e){
SqlConnection conn = new SqlConnection("Data Source=.; Initial Catalog=TestDb; Integrated Security=SSPI;");
conn.Disposed += Conn_Disposed;
conn.Open();
decimal m = 1;
try
{
decimal x = 3 / (m - 1); // 出错
}
catch(Exception)
{
}
finally
{
conn.Close();
conn.Dispose();
}
}
private void Conn_Disposed(object sender, EventArgs e)
{
MessageBox.Show("conn disposed"); // 仍然执行到这里了。
}
代码就会比较复杂。
有人说,第一段代码有啥意思呀,都没防住除以 0的异常,最终为了程序稳健,还不是要加 try,的确,但是我们要搞清楚 using的目的,using是一把保险锁,万一我们哪里没有加 try,哪里触发了异常,导致程序终止,非托管资源仍然能够得到释放。
有人说,using不要嵌套,因为内层如果发生了异常,外层的 using发挥不了作用。这是错误的说法。
看代码:
private void button1_Click(object sender, EventArgs e){
using (SqlConnection conn = new SqlConnection("Data Source=.; Initial Catalog=TestDb; Integrated Security=SSPI;"))
{
conn.Disposed += Conn_Disposed;
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Disposed += Cmd_Disposed;
decimal m = 1;
decimal x = 3 / (m - 1); // 出错
}
conn.Close();
}
}
private void Cmd_Disposed(object sender, EventArgs e)
{
MessageBox.Show("cmd disposed"); // 这里仍然执行了
}
private void Conn_Disposed(object sender, EventArgs e)
{
MessageBox.Show("conn disposed"); // 这里仍然执行了
}
先看到对话框:cmd disposed
再看到对话框:conn disposed
最后是异常对话框。