对不能用using的成对操作,快速扩展IDisposable的方法

日常操作中有很多需要打开/关闭   加锁/解锁的成对操作

有时候一些操作原生支持  IDisposable
Monitor 可以用Lock(){}   但是ReadWriteLock 就难了。 还有WCF Channel等)。
这种情况就要用  try/catch/finally,很是丑

封装成IDisposable可能很烦,因为多一个对象要多好多文档。

虽然AOP可能解决一些问题, 但是又没办法精确定位  scrope. 
还是 IDisposable +using最爽

 

所以写了一个缺省实现。

/// <summary> 
 
/// 销毁帮手,生成可以支持using的自定义IDisposable实例
/// <remarks>感谢网友@doggo对于 +=OnDispose功能的測試,由於不完善這裡決定取消該功能</remarks>
 
/// </summary> 
 
public struct Disposable : IDisposable
{
 
 
 
    /// <summary> 
 
    /// 创建销毁帮手实例 
 
    /// </summary> 
 
    /// <param name="onCreate">创建时要做的操作</param> 
 
    /// <param name="onDispose">销毁是要做的操作</param> 
 
    public Disposable(Action onCreate, Action onDispose)
    {
        OnDispose = onDispose;
 
 
        onCreate();
 
    }
 
 
 
    /// <summary> 
 
    /// 销毁时要做的操作                 /// </summary> 
 
    private Action OnDispose
    {
 
            get ;set;
 
         
 
    ////// <summary> 
 
    ////// 销毁时要做的操作  支持+=/Addhandler附加操作 (撤銷) 
 
    ////// </summary> 
 
    //////public event Action OnDispose ;
 
 
    #region IDisposable 成员
 
 
 
    void IDisposable.Dispose()
    {
 
     
        OnDispose();
 
        OnDispose = null;
 
    }
 
 
 
    #endregion
 
 
 
}

 

 

思路是用一个扩展方法,给一个无dispose 能力的对象 建立一个IDisposable的引用。

由于onCreate onDispose是闭包 额外的参数也是非必要的。

 

这里提供一个简易读写锁的实现。  大家可以参考

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public static class ReaderWriteerLockSlimHelper
{
    /// <summary>
    /// 为读写锁创建支持using的IDisposable帮手
    /// </summary>
    /// <param name="instance">读写锁实例</param>
    /// <param name="lockType">加锁类型 读/写</param>
    /// <returns>帮手实例</returns>
    public static IDisposable CreateDisposable(this ReaderWriterLockSlim instance, LockType lockType)
    {
        var kvp = LockDisposeDic[lockType];
        return new Disposable(() => kvp.Key(instance), () => kvp.Value(instance));
    }
 
    /// <summary>
    /// 读写的不同操作字典
    /// </summary>
    static Dictionary<LockType, KeyValuePair<Action<ReaderWriterLockSlim>, Action<ReaderWriterLockSlim>>> LockDisposeDic = new Dictionary<LockType, KeyValuePair<Action<ReaderWriterLockSlim>, Action<ReaderWriterLockSlim>>>()
    {
        {
            LockType.Read,
            new KeyValuePair<Action<ReaderWriterLockSlim>,Action<ReaderWriterLockSlim>>
                (
                    ins=>ins.EnterReadLock(),
                    ins=>ins.ExitReadLock()
                )
            
        },
        {
            LockType.Write,
            new KeyValuePair<Action<ReaderWriterLockSlim>,Action<ReaderWriterLockSlim>>
                (
                    ins=>ins.EnterWriteLock(),
                    ins=>ins.ExitWriteLock()
                )
            
        }
    };
         
}
 
public enum LockType
{
    Read,
    Write
}

 

 

实际使用起来就是爽。 这是一个在需要并发访问的队列中 加入对象的方法。

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/// <summary>
        /// 加入有序队列。
        /// </summary>
        /// <param name="item">加入的项目</param>
        public void Enqueue(TValue item)
        {
            using (_lock.CreateDisposable(LockType.Write))
            {
                Queue<TValue> enqueueTarget;
                var key=_keySelector(item);
                if (!_items.ContainsKey(key))
                {
                    var sortValue = _sortValueSelector(item);
                    if (!_index.TryGetValue( _sortValueSelector(item) ,out enqueueTarget ))
                    {
                        enqueueTarget = new Queue<TValue>();
                        _index.Add(sortValue, enqueueTarget);
                    }
 
                    enqueueTarget.Enqueue(item);
                    _items.Add(key, item);
                }
                else
                {
                    throw new InvalidOperationException("this Item already in queue");
                }
            }
 
        }

 

个人工作分享。希望能够帮助大家提高工作效率

 

附: 容易漏调结束行为的实现  和try catch finally 实现  大家比较下

  

 

   出异常会死锁的错误成对掉用 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/// <summary>
/// 加入有序队列。
/// </summary>
/// <param name="item">加入的项目</param>
public void Enqueue1(TValue item)
{
    _lock.EnterWriteLock();
 
 
        Queue<TValue> enqueueTarget;
        var key = _keySelector(item);
        if (!_items.ContainsKey(key))
        {
            var sortValue = _sortValueSelector(item);
            if (!_index.TryGetValue(_sortValueSelector(item), out enqueueTarget))
            {
                enqueueTarget = new Queue<TValue>();
                _index.Add(sortValue, enqueueTarget);
            }
 
            enqueueTarget.Enqueue(item);
            _items.Add(key, item);
        }
        else
        {
            throw new InvalidOperationException("this Item already in queue");
        }
        _lock.ExitReadLock(); //不但可能中途退出 还可能像这样写错解锁方法
 
 
}

 

   正确而麻烦的try catch finally

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/// <summary>
/// 加入有序队列。
/// </summary>
/// <param name="item">加入的项目</param>
public void Enqueue2(TValue item)
{
    _lock.EnterWriteLock();
 
    try
    {
        Queue<TValue> enqueueTarget;
        var key = _keySelector(item);
        if (!_items.ContainsKey(key))
        {
            var sortValue = _sortValueSelector(item);
            if (!_index.TryGetValue(_sortValueSelector(item), out enqueueTarget))
            {
                enqueueTarget = new Queue<TValue>();
                _index.Add(sortValue, enqueueTarget);
            }
 
            enqueueTarget.Enqueue(item);
            _items.Add(key, item);
        }
        else
        {
            throw new InvalidOperationException("this Item already in queue");
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        _lock.ExitWriteLock();
     
    }
 
 
}
posted @   MSFT:waywa 韦恩卑鄙  阅读(3081)  评论(23编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示