zoukankan      html  css  js  c++  java
  • 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的某个实现若抛出异常,其他多播的事件代理方法就不会执行了。

  • 相关阅读:
    博客阅读计数优化
    博客阅读简单计数
    博客后台富文本编辑
    博客分类统计
    Django关联关系查询
    上下篇博客,按月归档
    浅谈闭包以及常见面试题
    浅谈前端缓存(转至大佬)
    post请求头的常见类型
    浅谈RegExp 对象的方法
  • 原文地址:https://www.cnblogs.com/haokaibo/p/2129689.html
Copyright © 2011-2022 走看看