如何解决IReadOnlyDictionary缺乏协方差?

我正在尝试公开一个只读字典,该字典包含具有只读接口的对象。 在内部,字典是可写的,其中的对象也是可写的(参见下面的示例代码)。 我的问题是IReadOnlyDictionary不支持协变转换,因为这里的问题概述了原因。 这意味着我不能将我的内部字典公开为只读字典。

所以我的问题是,有没有一种有效的方法将我的内部字典转换为IReadOnlyDictionary,或者其他一些方法来处理它? 我能想到的选择是:

  1. 保留两个内部词典并使它们保持同步。
  2. 访问属性并在其中转换所有对象时创建新字典。
  3. 在内部使用它时,将IReadOnly转回NotReadOnly。

1看起来像是一种痛苦,2似乎非常低效。 3听起来像目前最有希望,但仍然是丑陋的。 我还有其他选择吗?

public class ExposesReadOnly { private Dictionary InternalDict { get; set; } public IReadOnlyDictionary 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; } } 

您可以为字典编写自己的只读包装器,例如:

 public class ReadOnlyDictionaryWrapper : IReadOnlyDictionary where TValue : TReadOnlyValue { private IDictionary _dictionary; public ReadOnlyDictionaryWrapper(IDictionary dictionary) { if (dictionary == null) throw new ArgumentNullException("dictionary"); _dictionary = dictionary; } public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); } public IEnumerable 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 Values { get { return _dictionary.Values.Cast(); } } public TReadOnlyValue this[TKey key] { get { return _dictionary[key]; } } public int Count { get { return _dictionary.Count; } } public IEnumerator> GetEnumerator() { return _dictionary .Select(x => new KeyValuePair(x.Key, x.Value)) .GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } 

我建议您可能希望定义自己的协变接口,并包含协变访问方法以及创建只读包装器对象的方法,该对象实现具有所需类型的IDictionaryIReadonlyDictionary 。 只需忽略界面中的IEnumerable>

根据你正在做的事情,定义IFetchByKey可能会有所帮助,它由IFetchByKeyinheritance, IFetchByKeyinheritance,前者接受任何类型对象的查询(给定一个对象实例,a Cat集合应该能够说明它是否包含该实例,即使它是一个类型DogToyotaPrius ;该集合将不包含后者类型的任何实例,并且应该能够这样说)。

也许这个解决方案适合您:

 public class ExposesReadOnly { private IDictionary InternalDict { get; set; } public IReadOnlyDictionary PublicList { get { IReadOnlyDictionary dictionary = new ReadOnlyDictionary(InternalDict); return dictionary; } } private class NotReadOnly : IReadOnly { public string Name { get; set; } } public void AddSomeValue() { InternalDict = new Dictionary(); 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!"; } } 

希望这可以帮助!

电贺

特定缺乏协方差的另一种方法:

解决idictionary上特定类型的有用协方差问题

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

根据您的使用情况,您可以通过公开Func来逃避。

 public class ExposesReadOnly { private Dictionary InternalDict { get; set; } public Func 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; } }