foreach,in(C# 参考)foreach, in (C# reference)

foreach 语句为类型实例中实现 System.Collections.IEnumerableSystem.Collections.Generic.IEnumerable<T> 接口的每个元素执行语句或语句块,如以下示例所示:The foreach statement executes a statement or a block of statements for each element in an instance of the type that implements the System.Collections.IEnumerable or System.Collections.Generic.IEnumerable<T> interface, as the following example shows:

var fibNumbers = new List<int> { 0, 1, 1, 2, 3, 5, 8, 13 };
int count = 0;
foreach (int element in fibNumbers)
{
    Console.WriteLine($"Element #{count}: {element}");
    count++;
}
Console.WriteLine($"Number of elements: {count}");

foreach 语句并不限于这些类型。The foreach statement isn't limited to those types. 可以将其与满足以下条件的任何类型的实例一起使用:You can use it with an instance of any type that satisfies the following conditions:

  • 类型具有公共无参数 GetEnumerator 方法,其返回类型为类、结构或接口类型。A type has the public parameterless GetEnumerator method whose return type is either class, struct, or interface type. 从 C# 9.0 开始,GetEnumerator 方法可以是类型的扩展方法Beginning with C# 9.0, the GetEnumerator method can be a type's extension method.
  • GetEnumerator 方法的返回类型具有公共 Current 属性和公共无参数 MoveNext 方法(其返回类型为 Boolean)。The return type of the GetEnumerator method has the public Current property and the public parameterless MoveNext method whose return type is Boolean.

下面的示例使用 foreach 语句,其中包含 System.Span<T> 类型的实例,该实例不实现任何接口:The following example uses the foreach statement with an instance of the System.Span<T> type, which doesn't implement any interfaces:

public class IterateSpanExample
{
    public static void Main()
    {
        Span<int> numbers = new int[] { 3, 14, 15, 92, 6 };
        foreach (int number in numbers)
        {
            Console.Write($"{number} ");
        }
        Console.WriteLine();
    }
}

从 C# 7.3 开始,如果枚举器的 Current 属性返回引用返回值ref T,其中 T 为集合元素类型),就可以使用 refref readonly 修饰符来声明迭代变量,如下面的示例所示:Beginning with C# 7.3, if the enumerator's Current property returns a reference return value (ref T where T is the type of a collection element), you can declare an iteration variable with the ref or ref readonly modifier, as the following example shows:

public class ForeachRefExample
{
    public static void Main()
    {
        Span<int> storage = stackalloc int[10];
        int num = 0;
        foreach (ref int item in storage)
        {
            item = num++;
        }

        foreach (ref readonly var item in storage)
        {
            Console.Write($"{item} ");
        }
        // Output:
        // 0 1 2 3 4 5 6 7 8 9
    }
}

从 C# 8.0 开始,可以使用 await foreach 语句来使用异步数据流,即实现 IAsyncEnumerable<T> 接口的集合类型。Beginning with C# 8.0, you can use the await foreach statement to consume an asynchronous stream of data, that is, the collection type that implements the IAsyncEnumerable<T> interface. 异步检索下一个元素时,可能会挂起循环的每次迭代。Each iteration of the loop may be suspended while the next element is retrieved asynchronously. 下面的示例演示如何使用 await foreach 语句:The following example shows how to use the await foreach statement:

await foreach (var item in GenerateSequenceAsync())
{
    Console.WriteLine(item);
}

默认情况下,在捕获的上下文中处理流元素。By default, stream elements are processed in the captured context. 如果要禁用上下文捕获,请使用 TaskAsyncEnumerableExtensions.ConfigureAwait 扩展方法。If you want to disable capturing of the context, use the TaskAsyncEnumerableExtensions.ConfigureAwait extension method. 有关同步上下文并捕获当前上下文的详细信息,请参阅使用基于任务的异步模式For more information about synchronization contexts and capturing the current context, see Consuming the Task-based asynchronous pattern. 有关异步流的详细信息,请参阅 C# 8.0 新增功能一文中的异步流部分。For more information about asynchronous streams, see the Asynchronous streams section of the What's new in C# 8.0 article.

foreach 语句块中的任何点上,可以使用 break 语句中断循环,或者可以使用 continue 语句继续执行到循环中的下一次迭代。At any point within the foreach statement block, you can break out of the loop by using the break statement, or step to the next iteration in the loop by using the continue statement. 还可以使用 gotoreturnthrow 语句退出 foreach 循环。You can also exit a foreach loop by the goto, return, or throw statements.

如果 foreach 语句应用为 null,则会引发 NullReferenceExceptionIf the foreach statement is applied to null, a NullReferenceException is thrown. 如果 foreach 语句的源集合为空,则 foreach 循环的正文不会被执行,而是被跳过。If the source collection of the foreach statement is empty, the body of the foreach loop isn't executed and skipped.

迭代变量的类型Type of an iteration variable

可以使用 var 关键字让编译器推断 foreach 语句中迭代变量的类型,如以下代码所示:You can use the var keyword to let the compiler infer the type of an iteration variable in the foreach statement, as the following code shows:

foreach (var item in collection) { }

还可以显式指定迭代变量的类型,如以下代码所示:You can also explicitly specify the type of an iteration variable, as the following code shows:

IEnumerable<T> collection = new T[5];
foreach (V item in collection) { }

在上述窗体中,集合元素的类型 T 必须可隐式或显式地转换为迭代变量的类型 VIn the preceding form, type T of a collection element must be implicitly or explicitly convertible to type V of an iteration variable. 如果从 TV 的显式转换在运行时失败,foreach 语句将引发 InvalidCastExceptionIf an explicit conversion from T to V fails at run time, the foreach statement throws an InvalidCastException. 例如,如果 T 是非密封类类型,则 V 可以是任何接口类型,甚至可以是 T 未实现的接口类型。For example, if T is a non-sealed class type, V can be any interface type, even the one that T doesn't implement. 在运行时,集合元素的类型可以是从 T 派生并且实际实现 V 的类型。At run time, the type of a collection element may be the one that derives from T and actually implements V. 如果不是这样,则会引发 InvalidCastExceptionIf that's not the case, an InvalidCastException is thrown.

C# 语言规范C# language specification

有关详细信息,请参阅 C# 语言规范中的 foreach 语句部分。For more information, see The foreach statement section of the C# language specification.

有关 C# 8.0 及更高版本中添加的功能的详细信息,请参阅以下功能建议说明:For more information about features added in C# 8.0 and later, see the following feature proposal notes:

请参阅See also