A quick way to expand IDisposable for pairwise operations that cannot be used

Original link: http://www.cnblogs.com/waynebaby/archive/2010/12/09/1900998.html

There are many pairs of operations that need to be opened / closed, locked / unlocked in daily operation.

Sometimes some operations are natively supported by IDisposable.
Monitor can use Lock() {} but ReadWriteLock is difficult. And WCF Channel).
In this case, try/catch/finally. It's ugly.

It can be annoying to encapsulate IDisposable, because more objects require more documents.

Although AOP may solve some problems, it can't precisely locate the profile.
Or IDisposable +using

 

So a default implementation is written.

    /// <summary>  

    ///Destroy the helper to generate a user-defined IDisposable instance that can support using
    ///< remarks > thank you @ doggo for testing the + = OnDispose function. Due to the imperfection, we decided to cancel the function < / remarks >

    /// </summary>  

    public struct Disposable : IDisposable
    {


   
        /// <summary>  

        ///Create destroy helper instance  

        /// </summary>  

        ///< param name = "oncreate" > what to do when creating < / param >  

        ///< param name = "ondispose" > destroy is the operation to be done < / param >  

        public Disposable(Action onCreate, Action onDispose)
        {
            OnDispose = onDispose;


            onCreate();

        }



        /// <summary>  

        ///What to do when destroying          
       /// </summary>  

        private Action OnDispose
        {

                get ;set;

             }  

        ////// <summary>  

        //////The operation to be done when destroying supports + = / Addhandler additional operation (undo)  

        ////// </summary>  

        //////public event Action OnDispose ;


        #region IDisposable member



        void IDisposable.Dispose()
        {

        
            OnDispose();

            OnDispose = null;

        }



        #endregion



    } 

 

 

The idea is to build an IDisposable reference to an object without dispose capability by using an extension method.

Because onCreate onDispose is a closure extra parameter is also unnecessary.

 

A simple read-write lock is provided here. You can refer to

 

 

    public static class ReaderWriteerLockSlimHelper 
    {
        /// <summary>
        ///Creating a using enabled IDisposable helper for read-write locks
        /// </summary>
        ///< param name = "instance" > read / write lock instance < / param >
        ///< param name = "locktype" > lock type read / write < / param >
        ///< returns > helper instance < / returns >
        public static IDisposable CreateDisposable(this ReaderWriterLockSlim instance, LockType lockType)
        {
            var kvp = LockDisposeDic[lockType];
            return new Disposable(() => kvp.Key(instance), () => kvp.Value(instance));
        }

        /// <summary>
        ///Different operation dictionaries for reading and writing
        /// </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
    }

 

 

It's really cool to use. This is a way to add objects to queues that require concurrent access.

 

 

/// <summary>
        ///Join an ordered queue.
        /// </summary>
        ///< param name = "item" > added items < / 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");
                }
            }

        }

 

Personal work sharing. Hope to help you improve your work efficiency

 

Attachment: implementation of easy to miss ending behavior and try catch finally implementation

  

 

Error of deadlock in case of exception

        /// <summary>
        ///Join an ordered queue.
        /// </summary>
        ///< param name = "item" > added items < / 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(); //It's not only possible to quit midway, but also possible to write wrong unlocking methods like this.


        }

 

try catch finally

        /// <summary>
        ///Join an ordered queue.
        /// </summary>
        ///< param name = "item" > added items < / 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();
            
            }


        }

Reproduced in: https://www.cnblogs.com/waynebaby/archive/2010/12/09/1900998.html

Added by Kurtismonger on Sat, 19 Oct 2019 22:28:25 +0300