List <t>是“更好”然后ICollection <t>?为什么要实现冗余接口?

时间:2015-10-18 19:06:45

标签: c#

Q1:List<T>ICollection<T>“更好”,更强大。为什么然后在Code第一篇教程中他们使用ICollection<T>?没有任何意义。 List<T>可以执行ICollection<T>等所有内容。是的,据我所知,它的界面和List是一个类。但是id没有回答这个问题。为什么不使用IList而不是ICollection?这也与以下问题有关......

Q2:List<T>定义为:

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>

其中IReadOnlyCollection<T>(例如)定义为

public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable

所以,它已经有IEnumerable<T>IEnumerable

为什么然后在IEnumerable<T>定义中使用 AGAIN IEnumerableList<T>

没有任何意义。

问题3:IList<T>的定义如下:

public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable

我不明白,如果List<T>实施IList<T>, 何时再次在ICollection<T>定义中使用IEnumerable<T>IEnumerableList<T>

将List定义为:

不会更短
public class List<T> : IList<T>

其中IList<T>已经实现了所有接口?

为什么要一次又一次地重复所有这些界面?

感谢。

5 个答案:

答案 0 :(得分:4)

<强> Q1:

List<T>可能更强大&#39;而不是ICollection<T>,但它不那么抽象。 如果ICollection<T>暴露了你需要的一切,那么为什么你要指定他必须使用List<T>从而限制你的接口/方法/用户的任何东西并限制他必须使用{{{ 1}?

您的类/界面的用户可能不会使用List<T>,而是使用List<T>,这也是HashedSet<T>&gt;但不是ICollection<T
如果他想将他的HashedSet传递给你的方法,那么如果你定义你的方法应该接受List而不是List<T>,那就意味着他必须将他的集转换为列表。

ICollection的特定性不如List,这意味着只要类型实现ICollection,您的方法就可以使用该类型。 如果ICollection指定了方法中所需的所有功能,则只能执行此操作 如果您需要能够使用索引器访问集合中的某个元素,那么ICollection<T>作为您的接口的参数类型不是一个好的选择,因为ICollection<T>没有定义这样的索引器。

Q2&amp; Q3:

所有关于抽象和Liskov substitution principle

如果你有一个接受ICollection<T>的方法,你可以为它分配一个IEnumerable,因为它在IReadonlyCollection旁边实现了IEnumerable。如果它只实现IEnumerable<T>,那么您将无法使用IEnumerable<T>作为参数来接受接受类型为IReadOnlyCollection<T>的参数的方法。

答案 1 :(得分:2)

使代码不那么具体,使其更强大。假设您有这样的方法:

void MyMethod(IList<int> items, int count, int start)
{
    while(items.Count < count)
    {
       items.Add(start++);
    }
}

该方法要求IList<int>,做一些List-y的事情,然后返回。但是,该方法不会对ICollection<int>

执行任何操作
void MyMethod(ICollection<int> items, int count, int start)
{
    while(items.Count < count)
    {
       items.Add(start++);
    }
}

这两种方法中哪一种更强大?

你可能会感到惊讶,但答案是第二个。 IList<int>实现ICollection<int>,因此您仍然可以将任何IList<int>传递给第二种方法。但是,您无法将任何ICollection<int>传递给第一个方法。以这种方式使接口相互依赖允许我们编写尽可能具体的方法,并以这种方式使我们的代码更强大,允许它使用更多类型的对象。

当您只需要特定于较少的操作时,遇到需要更具体类型的情况的唯一一次是在使用尚未理解这一点的团队编写的库时。

至于为什么文档显示所有不同的界面实现,你必须问语言设计师,但我猜他们只是想明确这一点,并让我们这些需要的人更容易阅读文档并验证IList确实实现了ICollection。

答案 2 :(得分:1)

首先,您应该定义更好的含义。 @github = Github.new :oauth_token => 'token', :org => 'org-name', :user => 'org-name', :repo => 'repo-name' @github.search.issues 'repo:user/repo type:pr state:closed updated:>=2015-10-15' 的功能比IList<T>更多。但拥有更多功能并不总是更好的选择。大多数情况下,最好使用功能最少的类型。

在这种情况下,具有满足要求的最小功能是更好的。例如,您可以提及类似ICollection<T>的集合类型作为更好的选择。虽然它具有更多功能(非常适合并发操作),但它也存在一些性能缺陷。

Q2&amp; Q3:对于重复的界面声明,我认为它们不是必需的,为了清楚起见,它们只是为了清楚而写。

答案 3 :(得分:1)

这只是Frederik答案的快速扩展。为什么一个人应该总是使用最通用的界面

public void Generalized(ICollection<string> collection)
{
    //blah
}

public void Specialized(List<string> collection)
{
    //blah
}

public void SomeOtherMethod()
{
    ICollection<string> coll = new List<string> { "a", "b" };
    List<string> list = new List<string> { "a", "b" };

    Generalized(coll);
    Generalized(list);

    Specialized(list);
    Specialized(coll); //doesn't compile
}

关于界面定义,它们可以让用户更清楚地了解List<T>所拥有的内容。

修改

要完成它,需要注意这里,当从方法返回和反对时,返回最专业的一个是明智的。

这一切都归结为分配兼容性可重用性。作为输入,采用最广义的一个,以便其他一切都可以被接受。返回时,返回最专业的一个,以便它适用于大多数调用方法。

public ICollection<string> ReturnGeneralized()
{
    return new List<string>();
}

public List<string> ReturnSpecialized()
{
    return new List<string>();
}

static void SomeOtherMethod()
{
    ICollection<string> coll1 = ReturnGeneralized();
    List<string> list1 = ReturnGeneralized(); //doesn't compile

    ICollection<string> coll2 = ReturnSpecialized();
    List<string> list2 = ReturnSpecialized();
}

所以,理想的方法应该是

public List<string> MostUsable(IEnumerable<string> collection)
{
    return new List<string>();
}

答案 4 :(得分:0)

  

List<T>可以完成ICollection<T>等所有内容。

不是真的。 ICollection<T>IList<T>之间的主要区别在于前者是无序,或者换句话说,元素的顺序不是由接口定义的。虽然后者提供了额外的功能(索引访问),但它同时对实现它的容器施加了约束(由于所需的列表契约不变量)。这意味着什么 - 列表的添加/删除操作非常低效(除了在列表的末尾,特别是在列表的开头)。另一方面,ICollection<T>没有这样的约束,因此容器可以自由地以最有效的方式实现操作。例如,另一个受欢迎的课程Dictionary<TKey, TValue>实现ICollection<KeyValuePair<TKey, TValue>>但不实现IList<KeyValuePair<TKey, TValue>>。它的强度是否低于List<T>?答案是不。到目前为止,没有一个容器可以有效地执行所有操作。另一个例子:搜索IList<T>ContainsIndexOf)非常低效。与HashSet<T>的{​​{1}}类实施相比,其中ICollection<T>ContainsAdd超快。如果它被迫实施Remove List`。

  

为什么然后在Code第一篇教程中他们使用IList<T>' (to make it more powerful by your logic) then the last 2 operations would suffer like in

我想你有实体框架。让我们思考。这些实体的主要目的是有效地支持称为CRUD(创建,读取,更新,删除)的4个主要数据库操作。正如您所看到的,没有索引访问(通常,db表与列表不同),因此对这些操作的实现者设置ICollection<T>约束是没有意义的。从所有标准接口IList<T>更接近客户端和实现者所需的最小功能。请注意,在填充实体时,没有什么可以阻止您 ICollection<T>分配给这些EF导航属性,但仍然允许EF在为您填充数据库中的数据时使用不同的容器。

总之,List<T>不适用于所有场景,这就是为什么存在更适合特定场景的其他接口的原因。 IList<T>是一个类,但绝对不是最好的 - 只是因为这样的东西不存在。

P.S。以上所有内容当然适用于 Q1 。那么 Q2 Q3 - 这只是一个品味问题。与某些人使用可选功能相同,例如显式 List<T>用于类字段,private用于类,internal访问类成员等,而其他人则不。结果是一样的。