为什么List<>实现了RemoveAll,但IList<>没有?

12
我正在重构我的代码,使用IList代替List。我在几个地方使用了List.RemoveAll,并注意到IList根本没有这个方法。这个做法有什么好的理由吗?

2
因为语言设计者认为并非所有实现 IList<T> 接口的类型都应该具有 RemoveAll(Predicate<T>) 方法。 - Yuval Itzchakov
因为I = 接口,包含你必须实现的抽象方法,请参见https://msdn.microsoft.com/en-us/library/system.collections.ilist(v=vs.110).aspx。 - AsfK
2
我不知道这个接口设计决策背后的原因(可能是为了最小化它们的接口大小?),但你总可以编写一个扩展方法,以涵盖任何 IList<> - Theodoros Chatzigiannakis
@AsfK:您能更具体地写出这与问题有什么关系吗? - O. R. Mapper
2个回答

10

软件工程中有一个原则叫做接口隔离。其核心理念是小接口比大接口更好。当这个想法被推到极致时,理想的接口只声明了一个成员-但我们不需要关心那个。重点是接口应该描述严格的需求而不是方便的特性。

在我们的具体情况中,IList<T>接口声明了一个类型必须实现的成员才能成为IList<T>。显然,一个类型并不需要实现RemoveAll才能成为IList。这对于一个类型来说是方便的,但不是必须的。

虽然这是扩展方法的一个有效用例。您可以为任何IList<T>定义自己的RemoveAll扩展方法,并保持方便。


谢谢!我理解设计原则。我的问题更多是关于这个特定情况——为什么System.Collections.Generic的设计者没有在IList接口中包含RemoveAll的原因是什么? - Sergey Kandaurov
@SergeyKandaurov 我猜是因为他们针对这个特定情况应用了上述设计原则,并且认为这不是他们想要强加给实现这些集合的人或与这些集合的消费者维护的契约。请记住,RemoveAll可以根据已公开的成员(而不知道其内部)来实现,因此没有理由要求集合为您实现它。 - Theodoros Chatzigiannakis
1
现在有意义了。那么我猜开发者应该有一些接口声明RemoveAll,以方便使用,而不是强制使用List<>或创建自定义扩展方法。 - Sergey Kandaurov

0

尽管Theodoros Chatzigiannakis解释了一般软件设计的原因,但当您确实需要RemoveAll时(也许您从库中收到了一个IList并希望以更简单的方式进行操作),您可能会感兴趣的解决方案在这里:https://github.com/dotnet/core/issues/2199

根据Dotnet核心团队的答案,我理解主要原因是遗留问题,但正如他们所说,您可以轻松编写扩展方法。这样一个扩展方法的例子(来源: https://www.extensionmethod.net/csharp/icollection-t/removeall

using System;
using System.Collections.Generic;
using System.Linq;

public static class CollectionExtensions
{
    public static void RemoveAll<T>(this ICollection<T> @this, Func<T, bool> predicate)
    {
        List<T> list = @this as List<T>;

        if (list != null)
        {
            list.RemoveAll(new Predicate<T>(predicate));
        }
        else
        {
            List<T> itemsToDelete = @this
                .Where(predicate)
                .ToList();

            foreach (var item in itemsToDelete)
            {
                @this.Remove(item);
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接