在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);
但是,由于该项已转换为另一个列表,因此“添加”不会执行任何操作,因为该项不会添加回原始结果对象

我可以想出一些解决方案,例如:

  • 我可以同步两个列表,但这似乎是额外的工作要做
  • 返回IEnumerable而不是IList,并添加用于创建/删除集合的方法
  • 创建一个采用T混凝土和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
    }
    
    公共接口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,我忘记提到了。