【问题标题】:How to get item from IEnumerable collection using its index in C#? [duplicate]如何使用 C# 中的索引从 IEnumerable 集合中获取项目? [复制]
【发布时间】:2018-11-02 14:39:46
【问题描述】:

我有IEnumerable 列表customerList 和索引。现在我想根据索引获取IEnumerable的项目。

例如,如果index = 3,它应该为我提供 IEnumerable 的第 3 项。

请指导。

IEnumerable<Customer> customerList = new Customer[] 
{ 
     new Customer { Name = "test1", Id = 999 }, 
     new Customer { Name = "test2", Id = 915 }, 
     new Customer { Name = "test8", Id = 986 },
     new Customer { Name = "test9", Id = 988 },
     new Customer { Name = "test4", Id = 997 },
     new Customer { Name = "test5", Id = 920 },
};

int currentIndex = 3;   //want to get object Name = "test8", Id = 986

【问题讨论】:

  • 不要在这里使用IEnumerable&lt;T&gt;。使用IList&lt;T&gt;
  • 你必须做customerList.ToList()[2];。请记住,索引从 0 开始,而不是 1。
  • @Icemanind: customerList.ToList() 可能会在我们只需要 2-nd 时创建一个 enourmous 列表(甚至抛出 OutOfMemory 异常)项目
  • @Icemanind 当数组已经是 IList 时,没有理由使用 ToList()。只需将其转换为正确的类型。更好的是,不要首先使用IEnumerable&lt;T&gt;
  • 我不明白"How to get the index of an element in an IEnumerable" 是如何与"How to get the item from an IEnumerable using its index" 重复的。这是两件不同的事情,答案也不相关。

标签: c# ienumerable


【解决方案1】:

例如,如果 index = 3,它应该为我提供第 3 项 IEnumerable

您知道基于 .NET 的索引为零吗?不过,你可以使用ElementAt

Customer c = customerList.ElementAt(currentIndex); // 4th

如果没有足够的项目,使用ElementAtOrDefault 来防止异常,然后你会得到null

Customer c = customerList.ElementAtOrDefault(currentIndex); // 4th or null

这些方法以使用IList&lt;T&gt; indexer 的方式进行了优化。因此,在您的示例中,实际上有一个 Customer[] 实现了该接口,因此它将使用索引器。

如果序列实现IList&lt;T&gt;,它将被枚举以在该索引处查找项目。

【讨论】:

  • 注意:ElementAtSystem.Linq 命名空间提供的扩展方法。 IE。您需要确保您的代码中有 using System.Linq; 语句。
  • 应该注意 ElementAt 可能不是 O(1)。
  • @Aron:添加了它。
【解决方案2】:
如果您想要基于索引的访问,

不要使用 IEnumerable。请改用IList

IList<Customer> customerList = new Customer[] 
{ 
 new Customer { Name = "test1", Id = 999 }, 
 new Customer { Name = "test2", Id = 915 }, 
 new Customer { Name = "test8", Id = 986 },
 new Customer { Name = "test9", Id = 988 },
 new Customer { Name = "test4", Id = 997 },
 new Customer { Name = "test5", Id = 920 },
};

对于 IEnumerable,唯一的选择是使用 ElementAt() 扩展方法。

IEnumerable&lt;T&gt; 只保证可以枚举一个集合。它没有其他关于访问元素的承诺。 ElementAt 允许通过枚举 一次收集一个项目来访问特定元素,直到达到所需的元素。这可能很昂贵。

幸运的是,ElementAt checks to see whether the argument is an IList 并尽可能使用基于索引的访问。

但这并不意味着您应该使用IEnumerable&lt;T&gt;。它会使不知道变量背后的实际实例是什么的维护者(包括您)感到困惑。如果将不是 IList 的东西分配给该变量,也会导致性能问题

【讨论】:

    【解决方案3】:

    这可以使用ElementAt 方法来实现。

    如果你的IEnumerable不是物化集合,而是生成序列,多次调用ElementAt方法会导致序列多次生成。这通常是不需要的,因为它会不必要地消耗资源。

    上面的一些答案建议使用IList 而不是IEnumerable。是的,它肯定会让通过索引访问变得不那么麻烦,但是使用IList 有一个令人讨厌的副作用,它会使集合变得可变。我会改用IReadOnlyList

    IReadOnlyList<Customer> customerList = new Customer[]
    {
        new Customer { Name = "test1", Id = 999 },
        new Customer { Name = "test2", Id = 915 },
        new Customer { Name = "test8", Id = 986 },
        new Customer { Name = "test9", Id = 988 },
        new Customer { Name = "test4", Id = 997 },
        new Customer { Name = "test5", Id = 920 },
    };
    
    int currentIndex = 3;   //want to get object Name = "test8", Id = 986
    
    var result = customerList[currentIndex];
    customerList.Add(new Customer()); // doesn't compile
    customerList[currentIndex] = new Customer(); // doesn't compile
    

    【讨论】:

      【解决方案4】:

      IEnumerable 是一种“流式”数据类型,因此请将其视为流而不是数组。

      var yourCustomer = customerList.Skip(2).First()
      

      但是,如果您想要一个更像数组的语法 IList 对于您的用例来说可能是一个更好的抽象。

      【讨论】:

      • “IEnumerable 是一种‘流式’数据类型” 不,这在任何地方都没有说。它只是所有可枚举事物的基本接口。您的实现只会阻止优化并确保枚举序列
      • 这是个坏主意。 IEnumerable不是流数据类型。已经可以通过ElementAt 访问元素,它完成了Skip.First 的组合作为最坏的情况。如果集合实际上是一个 IList,ElementAt 将使用索引访问。
      • 注意我说 IEnumerable 是一种流类型,因为接口有一个函数,GetEnumerator。 GetEnumerator 返回一个只能 MoveNext 和 Reset 的 IEnumerator。其他一切都是静态扩展方法。见:docs.microsoft.com/en-us/dotnet/api/…
      猜你喜欢
      • 2022-01-02
      • 2017-05-28
      • 2020-02-05
      • 1970-01-01
      • 1970-01-01
      • 2014-10-27
      • 2019-08-20
      • 2016-06-06
      • 1970-01-01
      相关资源
      最近更新 更多