从IEnumerable<T>集合中删除一个项

87

我有一个已经填充好的IEnumerable<User>集合。

我想从中删除一个项目,该怎么做?

foreach(var u in users)
{
  if(u.userId = 1123)
  {
    // remove!
  }
}

我知道在循环过程中不应该删除,所以我不介意创建一个新的集合或在循环后删除它。

但是我不知道如何删除一个项,有点困惑!

或者,我也感到困惑的另一个选择是,如何创建一个新的集合,例如:

IEnumerable<User> modifiedUsers = new List<User>();

foreach(var u in users)
{
   if(u.userId != 1233)
   {
        modifiedUsers.add ??????
   }
}

我该如何添加到收藏夹?


顺便提一下,在遍历列表时,如果不进行复制就删除元素,会导致运行时错误。就像Tilak在他的回答中展示的那样,必须在枚举之前复制列表。 - henon
9个回答

81

使用 LINQ 创建一个新的 List,而不是移除其中的某个元素:

// remove
users = users.Where(u => u.userId != 123).ToList();

// new list
var modified = users.Where(u => u.userId == 123).ToList();

8
两种情况下你都在创建一个新列表。第一个例子只是覆盖了引用。 - Willem D'Haeseleer
18
您的术语不正确。您并未从列表中删除项目,在这两个示例中,您都创建了一个新列表,并且旧列表保持不变。在第一个示例中,您仅覆盖引用,因此声明其结果为“remove”是不正确的。您应该更新您的问题以澄清这一点。 - Willem D'Haeseleer
2
显然,标题和作者想要删除该项,而不是创建一个没有它的新列表... - Eru
1
虽然这对我很有用,但我想指出作者要求从IEnumerable<T>中删除项目,因此示例中的后缀“.ToList()”是不必要的。(尽管可能是许多最终进入此页面的人所需的。) - egmfrs
始终使用 AsList() 而不是 ToList() - Alireza Sattari

37

您无法从 IEnumerable 中删除项目;它只能被枚举,如此处所述: http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.aspx

如果您想添加和删除项目,则必须使用 ICollection。也许您可以尝试将您的 IEnumerable 进行强制类型转换;当然,前提是基础对象实现了 ICollection。

有关 ICollection 的更多信息,请参见此处: http://msdn.microsoft.com/en-us/library/92t2ye13.aspx

当然,您可以像 lante 指出的那样,从您的 IEnumerable 创建一个新列表,但这可能是“次优”的,这当然取决于您的实际用例。

ICollection 可能是最好的选择。


13

尝试将 IEnumerable 转换为 List。从此时开始,你将能够使用 ListRemove 方法来移除项目。

要通过 Linq 将其作为参数传递到 Remove 方法中,可以使用以下方法获取项目:

  • users.Single(x => x.userId == 1123)
  • users.First(x => x.userId == 1123)

代码如下:

users = users.ToList(); // Get IEnumerable as List

users.Remove(users.First(x => x.userId == 1123)); // Remove item

// Finished

但是我可以将它分配给期望 ienumerable 的东西吗?之后我必须重新开始吗? - loyalflow
1
@user1361315 当然,List 实现了 IEnumerable 接口,因此你确实可以将它赋值给一个 IEnumerable。 - dutzu

10
你可以像这样做:
users = users.Where(x => x.userId != userIdToRemove);

3
您做不到。 IEnumerable<T> 只能被迭代。
在您的第二个示例中,您可以通过迭代原始集合的副本来删除。
foreach(var u in users.ToArray()) // ToArray creates a copy
{
   if(u.userId != 1233)
   {
        users.Remove(u);
   }
}

0

IEnumerable 接口只是可枚举的 - 它不提供任何方法来 AddRemove 或修改列表。

该接口只提供一种遍历某些项的方式 - 大多数需要枚举的实现都会实现 IEnumerable,例如 List<T>

为什么不直接使用您的代码而不进行到 IEnumerable 的隐式转换呢?

// Treat this like a list, not an enumerable
List<User> modifiedUsers = new List<User>();

foreach(var u in users)
{
   if(u.userId != 1233)
   {
        // Use List<T>.Add
        modifiedUsers.Add(u);
   }
}

我必须修改它,然后将其传递给期望一个IEnumerable的东西。 - loyalflow
然后修改它并传递它 - 没有理由不能将 List<T> 传递给期望 IEnumerable<T> 的方法,因为 List<T> 派生自 IEnumerable<T>。我甚至认为由于协变规则,您不需要进行转换。 - Charleh

0

-2

users.toList().RemoveAll(user => <你的条件>)


2
由于 .ToList(),这将把整个列表加载到内存中。应尽一切可能避免这种情况。 - miThom
这将仅从新创建的列表中删除元素,而不是原始可枚举对象。 - mBardos

-4
现在有一种扩展方法可以将 IEnumerable<> 转换为 Dictionary<,>,然后该字典就具有了 Remove 方法。
public readonly IEnumerable<User> Users = new User[]; // or however this would be initialized

// To take an item out of the collection
Users.ToDictionary(u => u.Id).Remove(1123);

// To take add an item to the collection
Users.ToList().Add(newuser);

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