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