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));