Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get around lack of covariance with IReadOnlyDictionary?

I'm trying to expose a read-only dictionary that holds objects with a read-only interface. Internally, the dictionary is write-able, and so are the objects within (see below example code). My problem is that IReadOnlyDictionary doesn't support covariant conversions because of the reason outlined in the question here. This means I can't just expose my internal dictionary as a read only one.

So my question is, is there an efficient way to convert my internal dictionary to an IReadOnlyDictionary, or some other way to handle this? The options I can think of are:

  1. Hold two internal dictionaries and keep them in sync.
  2. Create a new dictionary when the property is accessed and cast all the objects within.
  3. Cast the IReadOnly's back to NotReadOnly when using it internally.

1 seems like a pain, 2 seems highly inefficient. 3 sounds like the most promising at the moment, but is still ugly. Do I have any other options?

public class ExposesReadOnly {     private Dictionary<int, NotReadOnly> InternalDict { get; set; }     public IReadOnlyDictionary<int, IReadOnly> PublicList     {         get         {             // This doesn't work...             return this.InternalDict;         }     }      // This class can be modified internally, but I don't want     // to expose this functionality.     private class NotReadOnly : IReadOnly     {         public string Name { get; set; }     } }  public interface IReadOnly {     string Name { get; } } 
like image 506
Ocelot20 Avatar asked Nov 27 '12 21:11

Ocelot20


1 Answers

You could write your own read-only wrapper for the dictionary, e.g.:

public class ReadOnlyDictionaryWrapper<TKey, TValue, TReadOnlyValue> : IReadOnlyDictionary<TKey, TReadOnlyValue> where TValue : TReadOnlyValue {     private IDictionary<TKey, TValue> _dictionary;      public ReadOnlyDictionaryWrapper(IDictionary<TKey, TValue> dictionary)     {         if (dictionary == null) throw new ArgumentNullException("dictionary");         _dictionary = dictionary;     }     public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); }      public IEnumerable<TKey> Keys { get { return _dictionary.Keys; } }      public bool TryGetValue(TKey key, out TReadOnlyValue value)     {         TValue v;         var result = _dictionary.TryGetValue(key, out v);         value = v;         return result;     }      public IEnumerable<TReadOnlyValue> Values { get { return _dictionary.Values.Cast<TReadOnlyValue>(); } }      public TReadOnlyValue this[TKey key] { get { return _dictionary[key]; } }      public int Count { get { return _dictionary.Count; } }      public IEnumerator<KeyValuePair<TKey, TReadOnlyValue>> GetEnumerator()     {         return _dictionary                     .Select(x => new KeyValuePair<TKey, TReadOnlyValue>(x.Key, x.Value))                     .GetEnumerator();     }      System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()     {         return this.GetEnumerator();     } } 
like image 155
Joe Avatar answered Sep 28 '22 00:09

Joe