【发布时间】:2011-09-11 16:49:31
【问题描述】:
我在返回集合和协方差时遇到问题,我想知道是否有人有更好的解决方案。
场景是这样的:
我有 2 个版本的实现,我希望将版本实现完全分开(即使它们可能具有相同的逻辑)。在实现中,我想返回一个项目列表,因此在界面中,我将返回项目的接口列表。但是,在接口的实际实现中,我想返回item的具体对象。在代码中,它看起来像这样。
interface IItem
{
// some properties here
}
interface IResult
{
IList<IItem> Items { get; }
}
然后,将有 2 个命名空间具有这些接口的具体实现。例如,
命名空间版本 1
class Item : IItem
class Result : IResult
{
public List<Item> Items
{
get { // get the list from somewhere }
}
IList<IItem> IResult.Items
{
get
{
// due to covariance, i have to convert it
return this.Items.ToList<IItem>();
}
}
}
在命名空间Version2下会有另一个相同的实现。
要创建这些对象,将有一个工厂来获取版本并根据需要创建适当的具体类型。
如果调用者知道确切的版本并执行以下操作,则代码可以正常工作
Version1.Result result = new Version1.Result();
result.Items.Add(//something);
但是,我希望用户能够做这样的事情。
IResult result = // create from factory
result.Items.Add(//something);
但是,由于它已被转换为另一个列表,因此添加不会做任何事情,因为该项目不会被添加回原始结果对象。
我可以想到一些解决方案,例如:
- 我可以同步这两个列表,但这似乎是额外的工作
- 返回 IEnumerable 而不是 IList 并添加创建/删除集合的方法
- 创建一个采用 TConcrete 和 TInterface 的自定义集合
我理解为什么会发生这种情况(由于类型安全和所有),但我认为没有一种解决方法看起来很优雅。有人有更好的解决方案或建议吗?
提前致谢!
更新
想了很多之后,我觉得我可以做到以下几点:
public interface ICustomCollection<TInterface> : ICollection<TInterface>
{
}
public class CustomCollection<TConcrete, TInterface> : ICustomCollection<TInterface> where TConcrete : class, TInterface
{
public void Add(TConcrete item)
{
// do add
}
void ICustomCollection<TInterface>.Add(TInterface item)
{
// validate that item is TConcrete and add to the collection.
// otherwise throw exception indicating that the add is not allowed due to incompatible type
}
// rest of the implementation
}
那么我可以拥有
interface IResult
{
ICustomCollection<IItem> Items { get; }
}
then for implementation, I will have
class Result : IResult
{
public CustomCollection<Item, IItem> Items { get; }
ICustomCollection<TItem> IResult.Items
{
get { return this.Items; }
}
}
这样,如果调用者正在访问 Result 类,它将通过已经是 TConcrete 的 CustomCollection.Add(TConcrete item)。如果调用者通过 IResult 接口访问,它将通过 customCollection.Add(TInterface item) 并进行验证并确保类型实际上是 TConcrete。
我会试一试,看看是否可行。
【问题讨论】:
-
我认为使用选项 #2 将需要最少的代码量。它还会减少界面的表面积,因为实现不负责完整的 IList 支持,而调用者可能不需要。
-
你的解决方案看起来不错,但为什么你直接使用
ICustomCollection<T>而不是ICollection<T>? -
我完全可以。唯一的原因是我有一些专门用于收集的方法,这些方法不适用于需要通过内部代码访问的常规 ICollection
,我忘了提及。
标签: c# interface implementation covariance