异常安全在Java和C#语言中的简化

C簇语言中,C++首先在引入异常。在C++标准委员会投票之时,英国代表提出了反对意见,他们说异常差不多破坏了每个程序。如下的代码片段很好地展示了异常是如何把事情搞乱的

void f()
{
 T
* p = new T;
 
// 一些处理
 delete p;
}

 

如果处理过程引发异常抛出,那么语句“delete p;”便不会被执行,这就导致了内存泄漏。为解决这个问题,有两个提议,“智能指针”和垃圾收集。

英国人的反对意见是有道理,在C++中编写异常安全的代码,曾经困扰C++社群数年,97年至99年,终于总结了一些比较好的解决办法。至今为止,仍然是十分困难的事情。

Java和C#是一门大众化的语言,异常安全这么困难的技术,大多数程序员都会比较难以掌握,所以引入了一些简化的方式。

在Java中,引入了垃圾收集的技术,程序员不用关心内存的回收问题了。但是还是存在其他的一些资源,例如数据库连接、文件句柄、互斥体(Mutex)等等。Java中,为了简化对互斥体(Mutex)使用,引入了synchronized关键字(类似C#中的lock),使得线程的使用变得简单。如下:

public synchronized void f1(List list) {
 synchronized (list) 
{
    
//do something
 }

}

在C#中,则是更进了一步。为了解决异常安全问题,引入了lock、using两个关键字管理资源。lock与Java中的synchronized对应。

Object obj = new Object();
lock (obj)
{
    
//do something
}

上述代码在不用lock的时候,是这样写的:

System.Threading.Monitor.Enter(obj);
try
{
    
//do something
}

finally
{
    System.Threading.Monitor.Exit(obj);
}

而using则是一种和IDisposable配合使用的特别方式,或许是一种创新。
我们先看看IDisposable接口:

public interface IDisposable
{
      
// Methods
      void Dispose();
}


我们看看数据连接SqlConnection的实现,SqlConnection实现了IDisposable接口,SqlConnection中大约是这样实现:

if (disposing)
{
      
      
      
this.Close();
}

在Dispose中调用了Close方法,释放了资源。在File相关的对象,也是以类似的方式处理的。

使用Using:

using (IDbConnection conn = new SqlConnection())
{
    
// do something
}

如果不是用Using,也需要保证异常安全。可以这样写,与上面的代码等价:

IDbConnection conn = new SqlConnection();
try
{
    
// do something
}

finally
{
    IDisposable disposableObj 
= conn as IDisposable;
    
if (disposableObj != null)
    
{
        disposableObj.Dispose();
    }

}

当然更直接的代码是:

IDbConnection conn = new SqlConnection();
try
{
    
//do something
}

finally
{
    conn.Close();
}

在C#中,using的用法,并没有走到多远,其实就是try ... finally方式和IDiposible接口的配合使用。

其实foreach的关键字也起到简化编写异常安全代码的作用:

IList list = new ArrayList();
foreach (Object obj in list)
{
    
//do something 
}

 其实相当于:

IList list = new ArrayList();
IEnumerator iter 
= list.GetEnumerator();
try
{
    
while (iter.MoveNext())
    
{
        Object obj 
= iter.Current;
        
//do something 
    }

}

finally
{
    IDisposable disposableObj 
= iter as IDisposable;
    
if (disposableObj != null)
    
{
        disposableObj.Dispose();
    }

}

在foreach中,也使用了try ... finally,用于Dispose实现了IDisposable的IEnumerator对象。

Anders Hejlsberg曾说,在优秀的代码中,会有大量的try...finally形式的代码存在。为了减少try...finally的使用,C#引入了lock、using、foreach。

但是异常处理在C#中,缺少了异常规范(exception specification),这是令人遗憾,Anders Hejlsberg曾经发表过一些论述,很有道理,但是没有异常规范的C#是令人遗憾的。

posted on 2004-07-18 22:19  温少  阅读(1694)  评论(0编辑  收藏  举报

导航