如何清空IEnumerable列表?

16
我需要清空IEnumerable列表,我尝试了很多方法,如null等,但它们都没有起作用。
这是我的模型的外观。
public class NewsViewModel
{
    public NewsViewModel()
    {
        this.Categories = new List<Category>();
    }

    public int NewsId { get; set; }
    public string NewsTitle { get; set; }
    public string NewsBody { get; set; }

    public IEnumerable<Category> Categories { get; set; }
}

if (!string.IsNullOrEmpty(SelectedCategoriesIds))
{
    List<Category> stringList = new List<Category>();
    stringList.AddRange(SelectedCategoriesIds.Split(',').Select(i => new Category() { CategoryId = int.Parse(i) }));
    model.Categories = stringList.AsEnumerable();
}
else
{
    model.Categories = null;
}

如何使model.Categories为空?


1
我尝试了很多方法,比如使用null,但都没有起作用。...怎么办? - Rahul
当你只暴露 IEnumerable<T> 接口时,你的意图是下游使用这个接口来分发应该被枚举的东西。IEnumerable<T> 合同不包括任何与修改基础集合相关的成员。也许你应该暴露 ICollection<T> 接口? - spender
为什么需要将 List 转换为 IEnumerable?你不能在类中声明为 List<T> 吗?有特定的原因吗? - techspider
4个回答

47
使用Enumerable.Empty<T>()
model.Categories = Enumerable.Empty<Category>();

Empty()方法缓存了一个TResult类型的空序列。当返回的对象被枚举时,它不会产生任何元素。
没有元素的可枚举序列与null是不同的。如果你为IEnumerable返回null并尝试枚举它,你将收到一个NullReferenceException异常。
另一方面,如果你返回Enumerable.Empty()并尝试枚举它,代码将正常执行,无需进行null检查,因为没有需要枚举的元素。
值得注意的是,Enumerable.Empty()比返回new List()更高效,因为不需要分配新的列表对象。
在这种情况下,你不能使用.Clear()方法,因为你的IEnumerable是一个可投影的序列,在枚举之前不会实例化。还没有什么可以清除的。
最后,正如spender在下面提到的,这只会更新此特定引用。如果其他任何东西也持有对你的IEnumerable的引用,除非你通过ref特别传递model.Categories,否则它不会反映出更改。
或者,你可以强制转换为List并调用.Clear()方法,这将清除底层集合,更新所有引用。然而,在这样做时,你还需要进行明确的null检查,如其他答案中所述。但是,请注意,这也是一种非常激进的行为。你不仅更新了这个实例,而且还更新了所有可能具有副作用的实例。你需要根据意图和需求确定哪种方法更适合。

1
替换并没有清除。其他任何引用旧的 IEnumerable<Category> 的内容都不知道已经发生了这个变化。 - spender
1
model.Categories.Clear()会清空已有的IEnumerable。model.Categories = new IEnumerable<whatever>()将创建一个新的空IEnumerable。它可能不是可空类型 - 这就解释了为什么它不能设置为null。 - Dave Smash

4

只需创建一个空列表并将其分配即可。

model.Categories = new List<Category>();

2

如果要分配新值

您也可以使用空数组来完成此操作

  if (!string.IsNullOrEmpty(SelectedCategoriesIds))
  {
           List<Category> stringList = new List<Category>();
           stringList.AddRange(
                SelectedCategoriesIds.Split(',')
                     .Select(i => new Category() { CategoryId = int.Parse(i) }));
            model.Categories = stringList.AsEnumerable();
 }
 else
 {
      //set the property to an empty arrary
      model.Categories = new Category[]{};
 }

这种方法不如@DavidL的答案高效,但是是创建空枚举的另一种方式。

如果枚举总是列表并且您想要清除它而不是使属性类型为IList

如果您的模型始终具有列表并且您想要清除它而不是使其为空,则更改模型并以这种方式访问该方法。

 public class NewsViewModel
 {
    public NewsViewModel()
    {
        this.Categories = new List<Category>();
    }
    public int NewsId { get; set; }
    public string NewsTitle { get; set; }
    public string NewsBody { get; set; }

    public IList<Category> Categories { get; set; }

}

然后在您的if语句中,您可以使用clear。
else
 {
      //Now clear is available
      model.Categories.Clear();
 }

0

可以尝试这种方式:

(model.Categories as List<Category>)?.Clear();

那么,如果可枚举对象恰好是列表,则清除它,否则不执行任何操作?对我来说,这似乎不是一个非常明确的意图陈述。 - spender
无法保证 model.Categories 是一个列表,如果不是,则会静默失败。 - konkked
因此,如果您想要清除集合,则属性不应该是IEnumerable <T>,而应该是IList <T> - Udo

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