我试图给出IReadOnly
- 对内部Collection
个对象的引用。
这在大多数情况下效果很好,但如果我想将包含集合的字典转换为包含IReadOnlyDictionary
的{{1}},则不行。
这是一个代码示例:
IReadOnlyCollection
解决方法非常难看,因为它需要额外的内存,并且每当 var list = new List<int>();
IReadOnlyList<int> listReference = list; //works;
var dictionary = new Dictionary<int, int>();
IReadOnlyDictionary<int, int> dictionaryReference = dictionary; //works
var nestedList = new List<List<int>>();
IReadOnlyList<IReadOnlyList<int>> nestedReadOnlyListReference = nestedList; //works
var nestedDictionary = new Dictionary<int, List<int>>();
//IReadOnlyDictionary<int, IReadOnlyList<int>> nestedReadOnlyDictionaryReference = nestedDictionary; //does not work, can not implicitly convert
//current workaround
var nestedDictionaryReferenceHelper = new Dictionary<int, IReadOnlyList<int>>();
foreach (var kvpNestedDictionary in nestedDictionary)
{
nestedDictionaryReferenceHelper.Add(kvpNestedDictionary.Key, (IReadOnlyList<int>)kvpNestedDictionary.Value);
}
IReadOnlyDictionary<int, IReadOnlyList<int>> nestedReadOnlyDictionaryReference = nestedDictionaryReferenceHelper; //works, but is only a reference to the internal List, not to the dictionary itself
的值发生变化时都需要手动更新。
有没有简单的方法来转换这些嵌套的词典?
答案 0 :(得分:3)
In this SO question您可以找到一个非常好的解释,说明为什么不支持转换字典值。请参阅Eric Lippert接受的答案。
虽然我不建议这个,但可以使用以下LINQ表达式将字典的值强制转换为只读列表:
IReadOnlyDictionary<int, IReadOnlyList<int>> nestedReadOnlyDictionaryReference = nestedDictionary.ToDictionary(kv => kv.Key, kv => kv.Value as IReadOnlyList<int>);
它是您的变通方法的较短版本,并且它是惰性评估的,但由于以下原因,我不建议这样做:
这是不一致的行为,因此是一种不好的做法!
除非无法转换字典的值,否则我不建议这样做。您应该深度复制包括嵌套列表的整个字典,或者使用支持转换的其他容器。
答案 1 :(得分:1)
在我看来,重点在于你错失了引入适当的新类型并有自己尊严的机会。如果您正在使用Dictionary<int, List<int>>
,那么每次需要插入值时,您都会看到类似这样的代码:
if (!_dictionary.ContainsKey(key)) {
var list = new List<int>();
list.Add(value);
_dictionary.Add(key, list);
} else {
_dictionary[key].Add(value);
}
当你想要搜索一个值时,更糟糕的是使用这样的代码:
_dictionary.ContainsKey(key) && _dictionary[key].Contains(value);
这些例子的变化。更糟糕的是,您将这个实施细节暴露给您的班级用户。如果此细节发生变化,那么您将破坏所有代码。例如,如果您想将List<int>
替换为HashSet<int>
?
它应该如何?
_multimap.Add(key, value);
使用适当的界面(这里我只展示几种方法):
public interface IMultiMap<TKey, TValue> {
void Add(TKey key, TValue value);
bool ContainsKey(TKey key);
}
及其实施:
public sealed class MultiMap<TKey, TValue> : IMultiMap<TKey, TValue> {
// ...
private Dictionary<int, List<int>> _items;
}
您可以介绍IReadOnlyMultiMap<TKey, TValue>
:
public interface IReadOnlyMultiMap<TKey, TValue> {
bool ContainsKey(TKey key);
}
只需在IReadOnlyMultiMap<TKey, TValue>
中实现MultiMap<TKey, TValue>
并返回一个您无所事事的只读集合(虚构示例):
IReadOnlyMultiMap<int, int> MakeReadOnly(MultiMap<int, int> map) {
return map; // Nothing to do!
}
请注意,您可能希望引入一个新的ReadOnlyMultiMap<TKey, TValue>
来对底层实时集合进行隧道读取调用(以避免调用者只是强制转换为MultiMap<TKey, TValue>
来规避只读限制)。概念证明:
public sealed class ReadOnlyMultiMap<TKey, TValue> : IReadOnlyMultiMap<TKey, TValue> {
public ReadOnlyMultiMap(IMultiMap<TKey, TValue> collection) {
_collection = collection;
}
public bool ContainsKey(TKey key) {
return _collection.ContainsKey(key);
}
private readonly IMultiMap<TKey, TValue> _collection;
}
要返回只读视图,请执行以下操作:
IReadOnlyMultiMap<int, int> MakeReadOnly(MultiMap<int, int> map) {
return new ReadOnlyMultiMap<int, int>(map);
}
请注意,我谈到了实施细节。您仍然在公开实现细节(您正在使用多图),那么如果此类代码用于公共API,您应该引入一个新的(正确命名的)类型来描述它包含的内容,而不是如何实现存储< / strong>即可。它可能是MeasureCollection
,SoccerScoreCollection
或您的模型所讨论的内容,存储空间可能会有所不同,但内容则不会。
答案 2 :(得分:0)
转换失败的问题是KeyValuePair: 虽然类Derived继承类Base,但KeyValuePair不是KeyValuePair的子类;见定义(Dictionary,IReadOnlyDictionary)。
所以你总是需要某种解决方法(MultiMap方法在我看来也是一个......)。 如果 nestedDictionary
是私有的,那么您可以从课堂上完全控制它,您可能会放弃这个:
var nestedDictionary = new Dictionary<int, IReadOnlyList<int>>();
IReadOnlyDictionary<int, IReadOnlyList<int>> nestedReadOnlyDictionaryReference = nestedDictionary;
每当修改字典中的列表时,应用强制转换为List<int>
。我承认,另一个丑陋的解决方法,但为您节省了额外的内存和冗余管理,并保留了IReadOnlyDictionary<int, IReadOnlyList<int>>
的(假设...)公共接口。
编辑:只是一个想法,没有经过测试,但它可能工作:拥有自己的字典,添加缺少的接口,以便分配给只读字典:
public class MyDictionary
: Dictionary<int, List<int>>,
ICollection<KeyValuePair<int, IReadOnlyList<int>>,
IEnumerable<KeyValuePair<int, IReadOnlyList<int>>,
IReadOnlyCollection<KeyValuePair<int, IReadOnlyList<int>>
{
}
我可能还没有错过要实现的界面,而可能还必须实现一些成员。如果它有效,可能是最干净的解决方案......