Manipulate an IEnumerable

本文关键字:IEnumerable an Manipulate | 更新日期: 2023-09-27 17:55:43

我有一个类型为 IEnumerable 的输入参数。这不是IList.这不是一个ICollection<>.它是一个普通的IEnumerable.

此外,我需要找到一个项目并将其从IEnumerable中删除。

像这样:

foreach(var item in items)
{
    if (object.ReferenceEquals(item, data))
    {
       // something like this items.Remove(item); should happen here
    }

问题是items属于 IEnumerable 型。它没有删除方法。

如何删除项目?如何使用foreach中的项目,因为foreach在正确修改集合时会抛出错误?

Manipulate an IEnumerable

返回一个新的枚举,一个简短的实现可以是(使用 LINQ):

public IEnumerable<T> Remove<T>(IEnumerable<T> enumeration, T toRemove)
{
    return enumeration.Except(new[] { toRemove });
}

并像这样使用它:

annoyingEnumeration = this.Remove(annoyingEnumeration, annoyingItem);

对于非泛型枚举

public IEnumerable Remove(IEnumerable enumeration, object toRemove)
{
    foreach (var e in enumeration)
    {
        if (!object.ReferenceEquals(e, toRemove))
        {
            yield return e;
        }
    }
}

要将第二个作为IEnumerable接口上新的 Except -Linq 样式操作,您可以定义如下内容:

public static class ExtensionMethods
{
    public IEnumerable Without(this IEnumerable enumeration, object toLeaveOut)
    {
        foreach (var e in enumeration)
        {
            if (!object.ReferenceEquals(e, toLeaveOut))
            {
                yield return e;
            }
        }
    }
}

并像使用它一样使用

annoyingEnumeration = annyingEnumeration.Without(annoyingItem);

编辑

使用 100 个 IFS 检查实际类型

听起来是个坏主意,因为调用方可以给你一个自己的 IEnumerable 实现,而你不能对它做任何事情。当我只公开一个 IEnumerable 接口时,我将不允许消费者修改枚举,否则我会公开 ICollection 或 IList。

既然你已经说过创建一个新对象是不可接受的,并且你不能对对象进行比它实现IEnumerable更强的约束,那么这个问题是可以证明是无法解决的。 类型的底层实现可能以某种方式可变,您可以对其进行更改以删除该项,但同样可能不是。 即使是这样,即使有可能,进入IEnumerable的基础数据来改变它也是非常糟糕的做法。

相反,尽管你拒绝接受它,你几乎肯定需要并且应该创建一个新对象来表示你想要保留的所有项目;要么,要么提供一些更强的约束,关于你正在处理什么类型,这样你就会知道它是可变的, 以及如何变异它。

您应该基于

原始集合创建一个新集合。

var result = new List<object>();
foreach(var item in items)
{
    if (!object.ReferenceEquals(item, data))
    {
       result.Add(item);
    }
}

var filtered = items.Where(i => !object.ReferenceEquals(i, data));

您不能修改循环内的集合,我会将其转换为列表并操作列表,例如:

var custom = new ArrayList();    
foreach(var item in items)
{
    // invert the check
    if (!object.ReferenceEquals(item, data))
    {
       // add on the custom result
       custom.Add(item);
    }
}
return custom;

尝试以下扩展方法来删除项目。它返回 new IEnumerable ,包含原始集合中的所有项,但指定的项除外:

public static System.Collections.IEnumerable Remove<T>(this System.Collections.IEnumerable collection, T data)
{
    foreach (var item in collection)
        if (!object.ReferenceEquals(item, data))
            yield return item;
}

用法:

collection = collection.Remove(data);