2012-11-27 56 views
14

我試圖公開一個只讀的字典,其中包含只讀接口的對象。在內部,字典是可寫的,其中的對象也是可寫的(見下面的示例代碼)。我的問題是,由於問題here中列出的原因,IReadOnlyDictionary不支持協變轉換。這意味着我不能將我的內部字典暴露爲只讀字典。如何解決IReadOnlyDictionary缺乏協變性?

所以我的問題是,有沒有一種有效的方法來將我的內部字典轉換爲IReadOnlyDictionary或其他方式來處理?我能想到的選項有:

  1. 保持兩個內部字典並保持同步。
  2. 當屬性被訪問時,創建一個新的字典並將其中的所有對象進行轉換。
  3. 在內部使用IReadOnly時,將其重新轉換爲NotReadOnly。

1似乎是一種痛苦,2似乎非常低效。 3聽起來目前最有希望,但仍然很難看。我有其他選擇嗎?

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

回答

11

你可以寫在字典自己只讀的包裝,如:

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(); 
    } 
} 
+0

+1當我需要一個具有可變和不可變視圖的字典時,我做了這樣的事情。 –

+0

這更像是一本字典的只讀視圖。字典本身仍然可以改變。 – vidstige

+0

@vidstige,「字典本身仍然可以改變」 - 當然,這就是爲什麼它是私人的。 – Joe

0

也許這個解決方案適用於您:

public class ExposesReadOnly 
{ 
    private IDictionary<int, IReadOnly> InternalDict { get; set; } 
    public IReadOnlyDictionary<int, IReadOnly> PublicList 
    { 
     get 
     { 
      IReadOnlyDictionary<int, IReadOnly> dictionary = new ReadOnlyDictionary<int, IReadOnly>(InternalDict); 

      return dictionary; 
     } 
    } 

    private class NotReadOnly : IReadOnly 
    { 
     public string Name { get; set; } 
    } 

    public void AddSomeValue() 
    { 
     InternalDict = new Dictionary<int, NotReadOnly>(); 
     InternalDict.Add(1, new NotReadOnly() { Name = "SomeValue" }); 
    } 
} 

public interface IReadOnly 
{ 
    string Name { get; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     ExposesReadOnly exposesReadOnly = new ExposesReadOnly(); 
     exposesReadOnly.AddSomeValue(); 

     Console.WriteLine(exposesReadOnly.PublicList[1].Name); 
     Console.ReadLine(); 

     exposesReadOnly.PublicList[1].Name = "This is not possible!"; 
    } 
} 

希望這有助於!

電賀

+0

這適用於添加值,但我也可以在內部訪問它們並對它們進行更新。這就是我列出的我選擇的選項#3的地方,因爲當我在內部檢索它們時,我必須將它重新投射到NotReadOnly。 – Ocelot20

+0

通過我提供的解決方案,您可以完全訪問,更新,刪除,添加等。字典中的任何值都只能從類內部暴露出來,只能將其作爲只讀字典向外公開。請給出一個具體的例子,這個代碼可能不夠用,所以我可以更清楚地理解你的問題。 –

+0

'InternalDict [1] .Name =「SomeNewValue」'不會在沒有演員的情況下工作。 – Ocelot20

1

我建議你可能要定義自己的協接口,包括協變的接入方式以及一種將創建只讀包裝對象的方法,該對象實現具有所需類型的IDictionaryIReadonlyDictionary。在您的界面中忽略IEnumerable<KeyValuePair<TKey,TValue>>

根據您在做什麼,定義由IFetchByKey<in TKey, out TValue>繼承的IFetchByKey<out TValue>可能會有幫助,前者接受任何類型的對象的查詢(給定對象實例,Cat的集合應該能夠說明它是否包含該實例,即使它是DogToyotaPrius類型;該集合將不包含後者類型的任何實例,並且應該能夠這樣說)。

0

特定缺少協的另一種方法:

一種特定類型的有用的協方差的工作,周圍的IDictionary

public static class DictionaryExtensions 
    { 
     public static IReadOnlyDictionary<TKey, IEnumerable<TValue>> ToReadOnlyDictionary<TKey, TValue>(
      this IDictionary<TKey, List<TValue>> toWrap) 
     { 
      var intermediate = toWrap.ToDictionary(a => a.Key, a =>a.Value!=null? a.Value.ToArray().AsEnumerable():null); 
      var wrapper = new ReadOnlyDictionary<TKey, IEnumerable<TValue>>(intermediate); 
      return wrapper; 
     } 
    } 
0

根據你的使用情況,您可能能夠逃脫揭露了Func<int,IReadOnly>

public class ExposesReadOnly 
{ 
    private Dictionary<int, NotReadOnly> InternalDict { get; set; } 
    public Func<int,IReadOnly> PublicDictionaryAccess 
    { 
     get 
     { 
      return (x)=>this.InternalDict[x]; 
     } 
    } 

    // 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; } 
}