获取 IEnumerable 的第一项和 return 其余项作为 IEnumerable,仅迭代一次
Get the first item of an IEnumerable and return the rest as IEnumerable, iterating through only once
我有一个可枚举对象,其中包含来自逐渐进入的服务调用的响应。
- 我无法对可枚举对象执行
ToList
,因为那样会阻塞,直到收到所有响应,而不是在响应出现时列出它们。
- 我也不能迭代两次,因为那样会触发另一个服务调用。
如何获取可枚举的第一个元素和return可枚举的延续?我无法使用迭代器方法,因为出现编译错误:
Iterators cannot have ref, in or out parameters.
我试过这个代码:
public IEnumerable<object> GetFirstAndRemainder(IEnumerable<object> enumerable, out object first)
{
first = enumerable.Take(1).FirstOrDefault();
return enumerable.Skip(1); // Second interation - unexceptable
}
// This one has a compilation error: Iterators cannot have ref, in or out parameters
public IEnumerable<object> GetFirstAndRemainder2(IEnumerable<object> enumerable, out object first)
{
var enumerator = enumerable.GetEnumerator();
enumerator.MoveNext();
first = enumerator.Current;
while (enumerator.MoveNext())
{
yield return enumerator.Current;
}
}
除了使用 out
参数外,您还可以使用 ValueTuple<T1, T2>
(自 C# 7.0 起,已记录 here)到 return 两个元素:第一项IEnumerable<T>
,其余为另一个 IEnumerable<T>
。
using System.Linq;
class Program {
static void Main(string[] args) {
(int first, IEnumerable<int> remainder) = GetFirstAndRemainder(Enumerable.Range(1, 5));
// first = 1
// remainder yields (2, 3, 4, 5)
}
// Returns the first item and the remainders as an IEnumerable
static (T, IEnumerable<T>) GetFirstAndRemainder<T>(IEnumerable<T> sequence) {
var enumerator = sequence.GetEnumerator();
enumerator.MoveNext();
return (enumerator.Current, enumerator.AsEnumerable());
}
}
您还需要将 IEnumerator
转换为 IEnumerable
,这是我使用扩展方法完成的:
static class Extensions {
public static IEnumerable<T> AsEnumerable<T>(this IEnumerator<T> enumerator) {
while (enumerator.MoveNext()) {
yield return enumerator.Current;
}
}
}
请注意,根据您的要求,在 remainder
上迭代一次将耗尽它,即使它的类型为 IEnumerable<T>
.
我有一个可枚举对象,其中包含来自逐渐进入的服务调用的响应。
- 我无法对可枚举对象执行
ToList
,因为那样会阻塞,直到收到所有响应,而不是在响应出现时列出它们。 - 我也不能迭代两次,因为那样会触发另一个服务调用。
如何获取可枚举的第一个元素和return可枚举的延续?我无法使用迭代器方法,因为出现编译错误:
Iterators cannot have ref, in or out parameters.
我试过这个代码:
public IEnumerable<object> GetFirstAndRemainder(IEnumerable<object> enumerable, out object first)
{
first = enumerable.Take(1).FirstOrDefault();
return enumerable.Skip(1); // Second interation - unexceptable
}
// This one has a compilation error: Iterators cannot have ref, in or out parameters
public IEnumerable<object> GetFirstAndRemainder2(IEnumerable<object> enumerable, out object first)
{
var enumerator = enumerable.GetEnumerator();
enumerator.MoveNext();
first = enumerator.Current;
while (enumerator.MoveNext())
{
yield return enumerator.Current;
}
}
除了使用 out
参数外,您还可以使用 ValueTuple<T1, T2>
(自 C# 7.0 起,已记录 here)到 return 两个元素:第一项IEnumerable<T>
,其余为另一个 IEnumerable<T>
。
using System.Linq;
class Program {
static void Main(string[] args) {
(int first, IEnumerable<int> remainder) = GetFirstAndRemainder(Enumerable.Range(1, 5));
// first = 1
// remainder yields (2, 3, 4, 5)
}
// Returns the first item and the remainders as an IEnumerable
static (T, IEnumerable<T>) GetFirstAndRemainder<T>(IEnumerable<T> sequence) {
var enumerator = sequence.GetEnumerator();
enumerator.MoveNext();
return (enumerator.Current, enumerator.AsEnumerable());
}
}
您还需要将 IEnumerator
转换为 IEnumerable
,这是我使用扩展方法完成的:
static class Extensions {
public static IEnumerable<T> AsEnumerable<T>(this IEnumerator<T> enumerator) {
while (enumerator.MoveNext()) {
yield return enumerator.Current;
}
}
}
请注意,根据您的要求,在 remainder
上迭代一次将耗尽它,即使它的类型为 IEnumerable<T>
.