在c#中返回集合时如何处理协方差?
我在返回集合和协方差时遇到问题,我想知道是否有人有更好的解决方案 情况是这样的: 我有两个版本的实现,我希望将版本实现完全分开(即使它们可能具有相同的逻辑)。在实现中,我希望返回一个项目列表,因此在接口中,我将返回项目的接口列表。但是,在接口的实际实现中,我想返回该项的具体对象。在代码中,它看起来像这样在c#中返回集合时如何处理协方差?,c#,interface,implementation,covariance,C#,Interface,Implementation,Covariance,我在返回集合和协方差时遇到问题,我想知道是否有人有更好的解决方案 情况是这样的: 我有两个版本的实现,我希望将版本实现完全分开(即使它们可能具有相同的逻辑)。在实现中,我希望返回一个项目列表,因此在接口中,我将返回项目的接口列表。但是,在接口的实际实现中,我想返回该项的具体对象。在代码中,它看起来像这样 interface IItem { // some properties here } interface IResult { IList<IItem> Items
interface IItem
{
// some properties here
}
interface IResult
{
IList<IItem> Items { get; }
}
IResult result = // create from factory
result.Items.Add(//something);
但是,我希望用户能够这样做
interface IItem
{
// some properties here
}
interface IResult
{
IList<IItem> Items { get; }
}
IResult result = // create from factory
result.Items.Add(//something);
但是,由于该项已转换为另一个列表,因此“添加”不会执行任何操作,因为该项不会添加回原始结果对象
我可以想出一些解决方案,例如:
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
}
公共接口ICustomCollection:ICollection
{
}
公共类CustomCollection:ICustomCollection,其中T混凝土:类,TInterface
{
公共作废添加(t混凝土项目)
{
//添加
}
void ICustomCollection.Add(TInterface项)
{
//验证项目是否为T混凝土并添加到集合中。
//否则将引发异常,指示由于类型不兼容而不允许添加
}
//实施的其余部分
}
那我就可以
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; }
}
}
接口IResult
{
ICustomCollection项{get;}
}
那么为了实施,我将
类结果:IResult
{
公共CustomCollection项{get;}
ICustomCollection IResult.项目
{
获取{返回this.Items;}
}
}
这样,如果调用方正在访问结果类,它将遍历CustomCollection.Add(tcontcrete项),该项已经是tcontcrete。如果调用方是通过IResult接口访问的,它将通过customCollection.Add(TInterface项)进行验证,并确保类型实际上是T混凝土
我将尝试一下,看看这是否有效。您面临的问题是,您想公开一个类型,其中包括“您可以向我添加任何
项目”,但它实际会做的是“您只能向我添加项目”。我认为最好的解决方案是实际公开IList
。使用此选项的代码必须知道具体的项
,如果它应该将它们添加到列表中
但如果你真的想这么做,我认为最干净的解决方案是3,如果我理解正确的话。它将是IList
的包装器,它将实现IList
。如果您试图在其中放入非t混凝土的内容,它将抛出异常。+1对Brent的注释:返回不可修改的集合并提供类的修改方法
只是想重申为什么你不能让1以可解释的方式工作:
如果尝试添加到仅实现IItem
但不是Item类型(或派生自Item)的List
元素,则将无法在列表中存储此新项。结果,接口的行为将非常不一致——一些实现IItem的元素可以很好地添加,sime将失败,并且当您将实现更改为version2时,行为也将发生变化
简单的解决方法是将IList
存储在List
中,但直接公开collection需要仔细考虑。问题是,Microsoft使用一个接口IList来描述可以附加到的集合和不能附加到的集合。虽然从理论上讲,类可以以可变方式实现IList,也可以以不可变方式实现IList(前一个接口的ReadOnly属性将返回false,后一个将返回true),但类无法指定它以一种方式实现IList,以另一种方式实现IList where cat:T。我希望微软能让IList列表实现IReadableByIndex、IWritableByIndex、IReadWriteByIndex、iAppendeable和ICountable,因为这些都允许协方差和逆变,但它们没有。根据协方差的帮助程度,自行实现此类接口并为其定义包装可能会有所帮助。我认为使用选项2需要的代码量最少。它还将减少界面的表面积,因为实现不会提供调用方可能不需要的完整IList支持。您的解决方案看起来不错,但为什么要直接使用ICustomCollection
而不是ICollection
?我完全可以。唯一的原因是,我有一些专门针对colleciton的方法,这些方法不适用于需要通过内部代码访问的常规ICollection,我忘记提到了。