Making the Possible More Impossible

Jared Parsons thought it would be nice if we could have a ReadOnlyList object in our generic template instead of IEnumerable. I think that's an excellent idea, so a few minor modifications and we have an even better solution, not requiring the generic constraint TWrite : TRead:

    public interface ILockedObject<TRead, TWrite> 
    {
        void Read(Action<TRead> action);
        void Write(Action<TWrite> action);
        void Replace(ReplaceAction<TWrite> action);
    }
        
    public class ReaderWriterLockedObject<TRead, TWrite> : ILockedObject<TRead,TWrite>
    {
        public ReaderWriterLockedObject(TWrite value, Converter<TWrite, TRead> makeReadOnly)
        {
            MakeReadOnly = makeReadOnly;
            _value = value;
        }

        public Converter<TWrite, TRead> MakeReadOnly
        {
            get;
            private set;
        }

        TWrite _value;
        ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
        
        #region ILockedObject<T> Members

        public void Read(Action<TRead> action)

        {
            _rwLock.EnterReadLock();
            try
            {
                action(MakeReadOnly(_value));
            }
            finally
            {
                _rwLock.ExitReadLock();
            }
        }

        public void Write(Action<TWrite> action)
        {
            _rwLock.EnterWriteLock();
            try
            {
                action(_value);
            }
            finally
            {
                _rwLock.ExitWriteLock();
            }
        }

        public void Replace(ReplaceAction<TWrite> action)
        {
            _rwLock.EnterWriteLock();
            try
            {
                action(ref _value);
            }
            finally
            {
                _rwLock.EnterWriteLock();
            }
        }

        #endregion
    }

    public delegate void ReplaceAction<T>(ref T value);

 

Oh, and here is a ReadOnlyList<T> class to go along:

        public class ReadOnlyList<T> : IEnumerable<T>
        {
            IList<T> _list;
            public ReadOnlyList(IList<T> list)
            {
                _list = list;
            }

            public T this[int index]
            {
                get
                {
                    return _list[index];
                }
            }

            public int Count
            {
                get
                {
                    return _list.Count;
                }
            }
            
            #region IEnumerable<T> Members

            public IEnumerator<T> GetEnumerator()
            {
                return _list.GetEnumerator();
            }

            #endregion

            #region IEnumerable Members

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return _list.GetEnumerator();
            }

            #endregion
        }

So now we can do this:

ReaderWriterLockedObject<ReadOnlyList<Customer>, List<Customer>> lockedObject = new ReaderWriterLockedObject<ReadOnlyList<Customer>, List<Customer>>(l, c =>  new ReadOnlyList<Customer> (c));            

If we really want to be slick, we could take it a step further... protecting the elements inside our list:

ReaderWriterLockedObject<ReadOnlyList<ReadOnlyCustomer>, List<Customer>> lockedObject = new ReaderWriterLockedObject<ReadOnlyList<ReadOnlyCustomer>, List<Customer>>(l, wl =>  new ReadOnlyList<ReadOnlyCustomer> (wl, wc => (ReadOnlyCustomer)wc)); 

3 Comments

  • Code is not appearing corectly both n IE7 as well as FF

  • I like where you're taking this. But there is a problem with your ReadOnlyList implementation. It's not ReadOnly :(

    var list = new List();
    var roList = new ReadOnlyList(list);
    var count1 = roList.Count;
    list.Add(42);
    var count2 = roList.Count;
    // count1 != count2

    A read only list should be read only :)

  • You make a valid point, though if all access happens in Read and Write this shouldn't be an issue (since only a writer could have access to the writeable object). It is still possible though to circumvent the code if you hold references outside the delegate. You could potentially copy the list to be safe in the constructor, but you'd have to address the potential performance issues. An option in the constructor to specify whether to perform a copy would be a good addition (which I actually did over the weekend while implementing ReadOnlyDictionary :).

Comments have been disabled for this content.