【问题标题】:Cast between List<MyType> and List<IMyType>在 List<MyType> 和 List<IMyType> 之间转换
【发布时间】:2010-11-15 20:54:41
【问题描述】:

我正在尝试解决 C# 中缺乏对返回类型协方差的支持的问题,如this 问题及其前两个答案中所述。在大多数情况下,我在设置演员表时没有任何问题,但我使用对象/接口列表的一个属性阻碍了我的努力。

我需要做什么才能使IFoo.manyBars 的演员表工作?

public interface IBar
{
}

public interface IFoo
{
    IBar aBar { get; set; }
    IEnumerable<IBar> manyBars { get; set; }
}

class CBar : IBar
{
}

class CFoo : IFoo
{
    public CBar aBar { get; set; }


    //this cast works
    IBar IFoo.aBar 
    {
        get { return aBar; }
        set { aBar = (CBar)value; }
    }

    public List<CBar> manyBars { get; set; }

    //the compiler can't cast either of these
    List<IBar> IFoo.manyBars
    {
        get { return (List<IBar>)manyBars; }
        set { manyBars = (List<CBar>)value; }
    }
}

【问题讨论】:

    标签: c# interface casting


    【解决方案1】:

    试试这个。您必须将using System.Linq; 添加到源文件的顶部(如果它还没有的话)。

    List<IBar> IFoo.manyBars
    {
        get { return manyBars.Cast<IBar>().ToList(); }
        set { manyBars = value.Cast<CBar>().ToList(); }
    }
    

    请注意,这将在每次访问时分配并复制一个新数组到该属性。如果这不是您想要的,您应该考虑另一种方法,例如使用 IEnumerable&lt;IBar&gt; 类型公开属性。

    使用List&lt;IBar&gt; 也意味着有人可能会尝试执行anObject.manyBars.Remove(0),这将对anObject 中存储的列表完全没有,因为返回了一个副本。

    【讨论】:

    • 将我的界面切换到 IEnumerable 不是问题,这样做可以避免使用 Linq 进行强制转换导致的删除问题。
    • 某种——类的用户将无法删除项目,因为IEnumerable&lt;T&gt; 接口没有公开删除机制。因此,用户不那么惊讶,因为他们甚至无法尝试删除项目。
    • 我想我可以忍受,因为 IFoo/IBar 消费者是我试图封装的,并且向接口添加 Add/Remove 方法似乎不会造成任何重大问题.其余的 CFoo 和 COtherFoo 应用程序可以继续使用 List/List 版本。
    • 我遇到了一些奇怪的事情。虽然我可以直接使用我的简单示例类进行此转换 (IEnumerable&lt;IBar&gt;)manyBars,但对于我的应用程序中更复杂的类,转换失败,除非我首先将列表转换为数组,如下所示:(IEnumerable&lt;IBar&gt;)manyBars.ToArray()。我不知道我的班级是什么原因造成的,但因为即使在演员表中有额外的间接性,我也没有复制对象,我想这并不重要。
    • 您最好使用manyBars.Cast&lt;IBar&gt;(),因为这不会在每次通话时创建一个全新的列表。它更节省内存。
    【解决方案2】:

    您可以复制:

    get { return manyBars.OfType<IBar>().ToList(); } 
    set { manyBars = value.Cast<CBar>().ToList(); }
    

    但您不能强制转换该实例。如果您可以强制转换该实例,那么当我尝试 Add 一个不是 CBar 的 IBar 时会发生什么。

    【讨论】:

    • 我知道这有点脆弱,尽管在实践中 CFoo/CBar 和 COtherFoo/COtherBar 的用户将被隔离。与使用后者的相关项目共享为前者编写的大量代码是最后一分钟的需求更改,因此我在追求优雅的重构范围内受到限制。
    • 好吧,如果你想让它在出现问题时抛出,请使用 Cast。如果您希望它过滤掉无效项目,请使用 OfType。这几乎是您唯一的选择。
    【解决方案3】:

    根据您的需要,您可以创建一个新的退货列表:

    return new List<IBar>(manyBars);
    

    虽然请记住,虽然对象是相同的,但您将检索到不同的列表,因此您需要小心从列表中添加/删除对象。

    【讨论】:

    • 不幸的是,我的 IFoo 消费者需要能够从列表中添加/删除,因此创建新副本不是一个可行的选择。
    • 好的,但要小心,因为其他答案也有同样的问题。能否将列表声明更改为“public List manyBars { get; set; }”?
    • CBar 和 COtherBar 都包含许多未共享且 IBar 用户不关心的方法/属性,因此我无法更改 CFoo/COtherFoo 的定义以使用List,而不必在消耗 CFoo/COtherFoo 对象的代码库的所有(更大的)部分添加强制转换。
    猜你喜欢
    • 2012-11-29
    • 2012-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多