【问题标题】:Why does IList<> have fewer features than List<>?为什么 IList<> 的功能比 List<> 少?
【发布时间】:2023-04-02 19:10:01
【问题描述】:

要使用ConvertAll()这么大的功能,我得把IList转换成List,很痛苦。

【问题讨论】:

    标签: c# .net list generics


    【解决方案1】:

    请注意,List&lt;&gt;IList&lt;&gt; 的实际存储实现,即它在后台保存一个数组。一般来说,IList&lt;&gt; 可以是其他东西的代理。在 db4o 和 linq to sql 中,您的 IList&lt;&gt; 可以“指向查询”,即访问列表将触发数据库操作。

    这样,你可以执行myList.Skip(600).Take(20);来进行分页,只有在这一步才会执行实际的查询。包含一百万个条目的 List&lt;&gt; 将是巨大的,而可能有 IList&lt;&gt;s 具有巨大的 Count,但不要占用大量内存 - 只要您不访问这些元素。

    ConvertAll 将要求实例化每个对象,因此这是一项昂贵的操作。因此,最好使操作显式并强制您检索接口的特定实现。显然,转换无论如何都需要实例化所有对象,因此懒惰地进行它没有任何好处。

    【讨论】:

    • 很高兴了解 db4o 案例,我现在正在使用它。对于 db4o 情况,我不应该在使用 IList 之前关闭数据库,对吗?
    • 没错,你最终会得到一个DatabaseClosedException
    • Skip 和 Take 是 IEnumerable 方法,不一定是 IList。
    • 没错,我对此并不精确。由于IList&lt;T&gt; 继承了IEnumerable&lt;T&gt;IEnumerableICollection,因此很容易混淆:) 接口实际上只声明了IndexOf(),一个索引器[]InsertAt()RemoveAt()。感谢您指出这一点。
    【解决方案2】:

    为什么不用IEnumerable&lt;T&gt;.Select 而不是List&lt;T&gt;.ConvertAll?由于IList 继承IEnumerable。在 SO 上看到这个question

    【讨论】:

      【解决方案3】:

      因为接口定义了一个单一的行为,而一个类可以实现多个不同的接口,并且还具有接口未指定的特性。

      如果您想要 List&lt;T&gt; 类的功能,请不要使用 IList&lt;T&gt; 引用。从一开始就使用List&lt;T&gt; 引用。

      【讨论】:

        【解决方案4】:

        仅仅是因为IList(T) 是一个接口,而List(T) 是.net bcl 中实现IList(T) 以具有索引器功能的几个类之一。并非所有实现IList(T) 的类都需要ConvertAll() 方法,该方法用于将某个泛型类型的泛型列表转换为另一个。

        【讨论】:

          【解决方案5】:

          IList 接口旨在广泛实施。通过省略方便的方法,这意味着实现接口的工作更少,编写错误的机会也更少。

          幸运的是,LINQ 绕过了这一点,并通过“扩展方法”特性添加了一堆有用的方法。 SelectCast 对于转换目的特别有用。确保以 .NET Framework 3.5 为目标,引用 System.Core 程序集,并通过 using System.Linq; 看到它。

          【讨论】:

            【解决方案6】:

            接口中的每个方法或属性定义都会强制接口的每个实现为其提供代码。如果一个接口由全球一万个类实现,那么向接口添加一个只需要一行可执行代码来实现的方法将至少增加四万行所有这些实现所需的代码总量(假设正常的白色-间距约定)。相比之下,向类添加辅助方法对实现它的接口没有任何要求。

            顺便说一句,我对 .net 的主要愿望清单之一是接口可以通过静态方法将成员声明为具有默认实现的方法(例如,IFoo 包括成员 string Boz(int param) 可以指定如果寻求实现IFoo 的类的代码不包括该成员,编译器或运行时应自动生成方法string IFoo.Boz(int param) { return IFoo_Helpers.Boz(this, param);}。如果.net 2.0 版中存在这样的方法工具,它可以现在可能已经节省了数十万行代码,如果不是数百万行,只需让IEnumerable&lt;T&gt; 包含一个默认实现IEnumerator IEnumerable.GetEnumerator() {return IEnumerable_Helpers&lt;T&gt;.GetEnumerator(this);},后一种方法就是static IEnumerable GetEnumerator(IEnumerable&lt;T&gt; it) {return it.GetEnumerator();}。这样的特性将允许接口为他们的消费者提供更多的功能,而不需要对他们的实现者施加更多的工作。此外,与必须在调用站点静态绑定的扩展方法不同,此类接口方法将在实现中绑定,从而允许实现在适当的时候使用特定于离子的覆盖(例如,如果IEnumerable&lt;T&gt; 提供了ToList 方法,List&lt;T&gt; 可以定义一个快速实现,该实现创建一个新的List&lt;T&gt;,预初始化为适当的大小并使用@987654332 @ 来填充它,而 IEnumerable&lt;T&gt; 的实现将返回无穷无尽的序列可能会抛出异常(而不是吞噬他们可以获得的所有内存),但大多数实现不必对那个方法做任何事情——他们可以只需遵循默认的通用帮助函数,该函数会将项目枚举到新的List&lt;T&gt; 中,并让它根据需要增长。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-07-24
              • 1970-01-01
              相关资源
              最近更新 更多