我正在寻找是否有一种方法可以轻松地从 c# 类中公开我的容器,而不会泄露不必要的实现细节。我正在学习 C#,但我在其他 OO 语言方面经验丰富,所以我知道在可能的情况下,最好公开基类/接口而不是具体类型。我正在做的是在我的班级中存储密钥集合(对象集合)。我发现我可以轻松地将其公开为 IDictionary(key, 对象集合),但我想要的是 IDictionary(key, IEnumerable(object))。希望一个简单的例子能让它更清楚。
class AddressBook
{
public IDictionary<string, List<string>> Name2Addresses // ok, but I want IDictionary<string, IEnumerable<string>>
{
get { return name2Addresses; }
}
public IEnumerable<string> GetAddresses(string name)// ok, but I really just want the convenience of exposing container as a "one-liner"
{
return name2Addresses[name];
}
public AddressBook()
{
name2Addresses = new Dictionary<string,List<string>>();
List<string> addresses = new List<string>(); // I'd prefer IList<string> addresses = ...
name2Addresses.Add("Anne Best", addresses);
addresses.Add("Home address");
addresses.Add("Work address");
}
Dictionary<string, List<string>> name2Addresses;
}
我知道在 OO 中将派生容器暴露为基容器是一个棘手的问题,因为例如,您可以通过容器基类添加不同的派生容器。但希望因为我想通过 IEnumerable 将容器公开为只读,所以可能有一种简单的方法可以做到这一点。这是我想要的代码。
public IDictionary<string, IEnumerable<string>> Name2Addresses
{
get { return name2Addresses; }
}
我尝试添加一个演员,因为编译器抱怨没有演员
public IDictionary<string, IEnumerable<string>> Name2Addresses
{
get { return (IDictionary<string, IEnumerable<string>>) name2Addresses; }
}
但后来我遇到了一个例外:
附加信息:无法将“System.Collections.Generic.Dictionary 1[System.String]]”类型的对象转换为“System.Collections.Generic.IDictionary
2[System.String,System.Collections.Generic.IList
1[System.String]]”类型2[System.String,System.Collections.Generic.IEnumerable
。
有任何想法吗?我希望有一种简单/优雅的方式来做到这一点,因为总的来说,从较低级别的 OO 语言迁移到 C# 是一种乐趣,在 C# 中可以更快、更容易地将我想要的东西付诸实践。这无论如何都不是一个阻碍,因为我可以只显示 List 而不是 IEnum 并相信自己不会滥用 List,但我想正确地做事,特别是因为我希望有一天其他人使用我的代码.
PS 我记得在某处读到 C# 最近改进了对这类事情的支持。如果这有什么不同,我在 VS2010 上。
编辑
我在询问之前做了一些搜索,但经过更多搜索后,我发现以前有人问过这个问题,在稍微不同的上下文中,为什么不能将 Dictionary<T1, List<T2>> 强制转换为 Dictionary<T1, IEnumerable<T2 >>? . 所以我的问题可能会归结为是否有一种只读 IDictionary 的方法(另请参阅Is there a read-only generic dictionary available in .NET?)
另一个编辑
正如 Alex G / code4life 所建议的那样,这将起作用:无需滚动我自己的只读字典类,我就可以创建一个新字典。例如
public IDictionary<string, IEnumerable<string>> Name2AddressesNewDictionary
{
get
{
return name2Addresses.ToDictionary(na=>na.Key, na=>na.Value.AsEnumerable());
}
}
这会导致每次访问时性能损失+创建新键值对(以及字典本身)的空间损失。加上额外的开发时间。一般来说,如果我正在使用一个属性(尤其是在幕后进行的工作),这似乎并不比公开一个列表更好。但作为一种方法/在其他情况下会很好。
第三次编辑
正如 Jens Kloster 建议的那样,将容器的定义更改为使用 IEnumerable 作为值
Dictionary<string, IEnumerable<string>> name2Addresses;
然后在你需要的时候强制执行。
((IList<string>)name2Addresses["Anne Best"]).Add("Alternative address");