Effective C# 学习笔记(四十七)对异常进行strong guarantee 策略处理

异常处理的三种策略

Basic guarantee:在抛出异常前,保证所有资源没有溢出,而且各个对象属性状态是合法状态;

Strong guarantee:基于Basic guarantee,其强调对象的属性状态还原为失败修改前的状态。(该策略确保了从异常中修复和简化异常处理操作);

No throw Exception guarantee:在方法中不抛出异常,所有的错误都在该方法中处理。

Strong guarantee的原则步骤是:

  1. 为要修改的数据创建Copy
  2. Copy的数据尽心修改,这些修改包括那些可能抛出异常的修改;
  3. 将拷贝的数据还原为原始数据。该过程不能抛出异常。

 

举例说明:

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的实现方法中,请不要抛出异常。

原因:

FinalizersDispose()若抛出异常,整个应用程序就会崩溃退出了。

delegate的某个实现若抛出异常,其他多播的事件代理方法就不会执行了。

原文地址:https://www.cnblogs.com/haokaibo/p/2129689.html