异常处理的三种策略
Basic guarantee:在抛出异常前,保证所有资源没有溢出,而且各个对象属性状态是合法状态;
Strong guarantee:基于Basic guarantee,其强调对象的属性状态还原为失败修改前的状态。(该策略确保了从异常中修复和简化异常处理操作);
No throw Exception guarantee:在方法中不抛出异常,所有的错误都在该方法中处理。
Strong guarantee的原则步骤是:
- 为要修改的数据创建Copy;
- 对Copy的数据尽心修改,这些修改包括那些可能抛出异常的修改;
- 将拷贝的数据还原为原始数据。该过程不能抛出异常。
举例说明:
public void PhysicalMove(string title, decimal newPay)
{
// Payroll data is a struct:
// ctor will throw an exception if fields aren't valid.
PayrollData d = new PayrollData(title, newPay,
this.payrollData.DateOfHire);
// if d was constructed properly, swap:
this.payrollData = d;
}
上面的代码通过创建一个临时的变量d来缓存构造函数的结果,在其成功后,再对成员变量赋值。这样即使构造失败,其也不会对成员变量产生影响。
需要注意的是,对于循环等需要大量缓存Copy数据的逻辑来说,若在循环中出现异常则程序就会退出的话,就没有必要去缓存那么多Copy数据。否则即使会有些性能上的损失,也建议在这种逻辑操作中缓存大量的Copy数据。
另外,对于引用类型的属性,是不能通过上面的Swap操作来实现strong guarantee异常策略的。因为引用类型的属性直接暴露给客户端的话,其在多线程等多并发更改中,可能无法保证还原到原始的属性状态(因为其可能在恢复过程中被其他线程通过引用修改了)。
你需要通过Envelope Letter 模式来实现对于引用属性的异常处理的strong guarantee策略。代码如下:
//首先这里通过创建Envelope类型对象对受保护数据进行封装
private Envelope data;
public IBindingList MyCollection
{
//对于该Envelope对象进行只读属性封装
get
{
return data;
}
}
public void UpdateData()
{
//在更新时只调用Enveloper的更新方法来执行可能抛出异常的操作更新受保护的数据
data.SafeUpdate(UnreliableOperation());
}
//Envelope类型实现了IBindingList接口,并把受保护的数据作为私有属性封装起来,通过实习IBindingList接口的方法,提供对受保护数据data的访问控制,其结构如下:
public class Envelope : IBindingList
{
private BindingList<PayrollData> data = new BindingList<PayrollData>();
#region IBindingList Members //对外暴露该接口的方法
public void AddIndex(PropertyDescriptor property)
{ (data as IBindingList).AddIndex(property); }
public object AddNew() { return data.AddNew(); }
public bool AllowEdit { get { return data.AllowEdit; } }
public bool AllowNew { get { return data.AllowNew; } }
public bool AllowRemove
{ get { return data.AllowRemove; } }
public void ApplySort(PropertyDescriptor property,
ListSortDirection direction)
{ (data as IBindingList).ApplySort(property, direction); }
public int Find(PropertyDescriptor property, object key)
{ return (data as IBindingList).Find(property, key); }
public bool IsSorted
{ get { return (data as IBindingList).IsSorted; } }
private ListChangedEventHandler listChangedHandler;
public event ListChangedEventHandler ListChanged
{
add { listChangedHandler += value; }
remove { listChangedHandler -= value; }
}
public void RemoveIndex(PropertyDescriptor property)
{ (data as IBindingList).RemoveIndex(property); }
public void RemoveSort()
{ (data as IBindingList).RemoveSort(); }
public ListSortDirection SortDirection
{ get { return (data as IBindingList).SortDirection; } }
public PropertyDescriptor SortProperty
{get { return (data as IBindingList).SortProperty;}}
public bool SupportsChangeNotification
{ get { return (data as IBindingList).SupportsChangeNotification; } }
public bool SupportsSearching
{get {return (data as IBindingList).SupportsSearching;}}
public bool SupportsSorting
{get {return (data as IBindingList).SupportsSorting;}}
#endregion
#region IList Members
public int Add(object value)
{
if (value is PayrollData)
data.Add((PayrollData)value);
return data.Count;
}
public void Clear() { data.Clear(); }
public bool Contains(object value)
{
if (value is PayrollData)
return data.Contains((PayrollData)value);
else
// If the argument isn't the right type,
// it must not be here.
return false;
}
public int IndexOf(object value)
{
if (value is PayrollData)
return data.IndexOf((PayrollData)value);
else
return -1;
}
public void Insert(int index, object value)
{ if (value is PayrollData)
data.Insert(index, (PayrollData)value); }
public bool IsFixedSize
{ get { return (data as IBindingList).IsFixedSize; } }
public bool IsReadOnly
{ get { return (data as IBindingList).IsReadOnly; } }
public void Remove(object value)
{
if (value is PayrollData)
data.Remove((PayrollData)value);
}
public void RemoveAt(int index)
{ data.RemoveAt(index); }
public object this[int index]
{
get { return data[index]; }
set {
if (value is PayrollData)
data[index] = (PayrollData)value;
}
}
#endregion
#region ICollection Members
public void CopyTo(Array array, int index)
{ (data as System.Collections.ICollection). CopyTo(array, index); }
public int Count { get { return data.Count; } }
public bool IsSynchronized
{ get { return (data as System.Collections.ICollection).IsSynchronized;}}
public object SyncRoot
{ get { return (data as
System.Collections.ICollection).SyncRoot; } }
#endregion
#region IEnumerable Members
public System.Collections.IEnumerator GetEnumerator()
{ return data.GetEnumerator(); }
#endregion
//更新方法
public void SafeUpdate(IEnumerable<PayrollData>
bindingList)
{
// make the copy:
BindingList<PayrollData> updates = new BindingList<PayrollData>(bindingList.ToList());
// swap: 这里用了多线程的原子操作模式来对受保护对象赋值
System.Threading.Interlocked.Exchange
<BindingList<PayrollData>>(ref data, updates);
}
}
使用no-throw guarantee策略的场景:
在Finalizers, Dispose()方法delegate的实现方法中,请不要抛出异常。
原因:
Finalizers和Dispose()若抛出异常,整个应用程序就会崩溃退出了。
delegate的某个实现若抛出异常,其他多播的事件代理方法就不会执行了。