List.AddRange调用了List.Add吗?

5

我有一个自定义类,派生自List,其中包含一个Add方法,只有在满足特定条件时才会添加。

我是否还需要覆盖AddRange方法,或者AddRange是否只是调用给定范围中每个元素的Add方法?

*:是的,在C#上下文中,new是隐藏而不是覆盖。


2
List<T>.Add方法不是虚方法,所以我想知道你是如何覆盖它的。 - Darin Dimitrov
@DarinDimitrov 使用 new - Superbest
1
@Superbest,“它确实有效”算作优点吗? - svick
1
无论 AddRange 是否调用 Add 都没有关系。它肯定不会调用你隐藏的 Add,而是调用被隐藏的 Add。但是这样做是不行的。这是一个巨大的 LSP 违规。从 Collection<T> 派生怎么会是“更多的工作”呢? - CodesInChaos
1
@Superbest 从 Collection<T> 派生是实现 IList<T> 的特殊情况,旨在简化您的具体问题。 - CodesInChaos
显示剩余4条评论
3个回答

9
如果您想创建自定义集合,请不要从List<T>派生,而应该从Collection<T>或直接实现IList<T>ICollection<T>。事实上,List<T>类中的Add方法不是虚方法。
注意:List<T>.AddRange使用Array.Copy
更新:
当继承Collection时,只需要重写2个方法!
public class MyCollection : Collection<string>
{
    private bool IsValidItem(string item)
    {
        return; // Your condition : true if valid; false, otherwise.
    }

    // This method will be called when you call MyCollection.Add or MyCollection.Insert
    protected override void InsertItem(int index, string item)
    {
        if(IsValidItem(item))
            base.InsertItem(index, item);
    }

    // This method will be called when you call MyCollection[index] = newItem
    protected override void SetItem(int index, string item)
    {
        if(IsValidItem(item))
            base.SetItem(index, item);
    }
}

如果你要验证的项目不是字符串,请在上面的代码中将string替换为正确的类型。

1
@svick 不应该派生自 _List<T>,因为它的方法不是虚拟的,与 Collection<T> 相反。_List<T>.AddRange 是使用 Array.Copy 实现的。 - Cédric Bignon
@CédricBignon 哎呀,我真不敢相信我竟然没注意到缺少了“我”的字。最近我一直在忙于接口的工作... - Nolonar
我不明白为什么覆盖这两个方法比覆盖 Add 更好(除了 Collection 的作者声明他们想要我使用 virtual 之外)。除了 Add,是否还有其他调用 SetItemInsertItem 的方法?如果我想使用 InsertItem 在无需验证的情况下插入元素,并使用 Add 在需要验证时插入元素怎么办? - Superbest
1
@Superbest,你无法重写_Add_方法,因为_Add_不是虚方法,你只能隐藏它。 - Cédric Bignon
虽然我欣赏隐藏和覆盖的微妙差别,但在我的应用程序中这是无关紧要的。我确信我的自定义集合永远不会被强制转换或继承。这似乎有些奇怪,因为我可以只用一个方法来实现而不是重新实现两个方法。 - Superbest
显示剩余10条评论

4
不要使用会改变方法语义的隐藏方式,这是设计上的严重问题。
创建一个实现 IList<T> 的新类。最简单的方式是继承 Collection<T>Collection<T> 实现了 IList<T> 并提供了四个扩展点,形式为 protected virtual 方法:
InsertItem
SetItem
RemoveItem
ClearItems

因为您只需要验证添加的项目而不是删除的项目,所以只需覆盖InsertItemSetItem方法。

class MyCollection:Collection<T>
{
    private void ValidateItem(T item)
    {
       if(item is invalid)
         throw new ArgumentException("Item is invalid");
    }

    protected override InsertItem(int index, T item)
    {
        ValidateItem(item);
        base.InsertItem(index, item);
    }

    protected override SetItem(int index, T item)
    {
        ValidateItem(item);
        base.SetItem(index, item);
    }
}

这是非常糟糕的设计。 - 为什么? - Superbest
@Superbest 因为向基类转换规避了你的隐藏。 - CodesInChaos
违反了LSP,你不再拥有一致的实现(IList<T>.AddList<T>.AddMyList.Add不一致),等等。 - CodesInChaos
圆-椭圆这样的东西无论如何都违反了LSP原则,不是吗? - Superbest
@Superbest:是的,很多其他事情也是这样。你的观点是什么? - siride
@siride 我的观点是,“违反LSP”不一定是不这样做的理由。 - Superbest

2
如果您需要一个行为完全像 List<T> 的集合,但仅添加有效对象,那么我不会创建自定义集合。
相反,请使用扩展方法 ,并将它们命名为 AddIfValid(T value)AddRangeIfValid(IEnumerable<T>) 或任何你喜欢的名称,只要清楚地表明扩展方法正在做什么。
以下是一个示例:
public static void AddIfValid(this List<T> list, T value)
{
    if (/* check if value is valid here */)
        list.Add(value);
}

一旦您定义了扩展,就可以像这样使用它:

myList.AddIfValid(myValue);

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