C# 中有关yield 用法

443 阅读6分钟

今天来测试一下豆包和deepseek两个AI平台,顺便学习一下yield 用法。

一 先看看eepseek 平台

yield 是 C# 中的一个关键字,用于简化迭代器的实现。它允许你以更简洁的方式生成一个序列(IEnumerable 或 IEnumerator),而不需要显式地实现 IEnumerable 或 IEnumerator 接口。yield 通常用于生成器方法中,可以逐步返回序列中的元素。


1. yield 的基本用法

yield 关键字通常与 return 或 break 一起使用:

  • yield return:返回序列中的下一个元素。
  • yield break:终止序列的生成。

示例:生成一个简单的整数序列

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        foreach (int number in GetNumbers())
        {
            Console.WriteLine(number);
        }
    }

    static IEnumerable<int> GetNumbers()
    {
        yield return 1;
        yield return 2;
        yield return 3;
    }
}

输出:

1
2
3

2. yield 的工作原理

当编译器遇到 yield return 时,它会自动生成一个状态机来管理迭代器的状态。每次调用 MoveNext 方法时,迭代器会从上一次 yield return 的位置继续执行,直到遇到下一个 yield return 或 yield break

示例:生成无限序列

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        foreach (int number in GetInfiniteSequence())
        {
            Console.WriteLine(number);
            if (number > 10) break; // 防止无限循环
        }
    }

    static IEnumerable<int> GetInfiniteSequence()
    {
        int i = 0;
        while (true)
        {
            yield return i++;
        }
    }
}

输出:

0
1
2
3
4
5
6
7
8
9
10
11

3. yield 的延迟执行

yield 的一个重要特性是延迟执行(Lazy Evaluation)。序列中的元素只有在被请求时才会生成,而不是一次性生成所有元素。

示例:延迟执行

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        var numbers = GetNumbers();
        Console.WriteLine("Before foreach");

        foreach (int number in numbers)
        {
            Console.WriteLine(number);
        }
    }

    static IEnumerable<int> GetNumbers()
    {
        Console.WriteLine("Start generating");
        yield return 1;
        Console.WriteLine("Generated 1");
        yield return 2;
        Console.WriteLine("Generated 2");
        yield return 3;
        Console.WriteLine("Generated 3");
    }
}

输出:

Before foreach
Start generating
1
Generated 1
2
Generated 2
3
Generated 3

可以看到,GetNumbers 方法中的代码是在 foreach 循环中逐步执行的,而不是一次性执行完毕。


4. yield break 的使用

yield break 用于提前终止序列的生成。

示例:使用 yield break 终止序列

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        foreach (int number in GetNumbers(5))
        {
            Console.WriteLine(number);
        }
    }

    static IEnumerable<int> GetNumbers(int max)
    {
        for (int i = 0; i < 10; i++)
        {
            if (i >= max)
                yield break; // 提前终止

            yield return i;
        }
    }
}

输出:

0
1
2
3
4

5. yield 的常见应用场景

  • 生成序列:例如生成斐波那契数列、素数序列等。
  • 分页加载数据:逐步从数据库或文件中读取数据。
  • 延迟加载:只有在需要时才生成数据,节省内存和计算资源。

示例:生成斐波那契数列

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        foreach (int number in Fibonacci(10))
        {
            Console.WriteLine(number);
        }
    }

    static IEnumerable<int> Fibonacci(int count)
    {
        int a = 0, b = 1;
        for (int i = 0; i < count; i++)
        {
            yield return a;
            int temp = a;
            a = b;
            b = temp + b;
        }
    }
}

输出:

0
1
1
2
3
5
8
13
21
34

6. 注意事项

  • 性能yield 生成的序列是延迟执行的,适合处理大数据集或需要逐步生成数据的场景。
  • 线程安全yield 生成的迭代器不是线程安全的。如果需要在多线程环境中使用,需要手动加锁。
  • 资源释放:如果迭代器方法中使用了需要释放的资源(如文件句柄、数据库连接等),建议使用 try-finally 块来确保资源释放。

示例:资源释放

using System;
using System.Collections.Generic;
using System.IO;

class Program
{
    static void Main()
    {
        foreach (string line in ReadLines("test.txt"))
        {
            Console.WriteLine(line);
        }
    }

    static IEnumerable<string> ReadLines(string filePath)
    {
        using (var reader = new StreamReader(filePath))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                yield return line;
            }
        }
    }
}

总结

yield 是 C# 中实现迭代器的强大工具,它简化了生成序列的代码,并支持延迟执行和逐步生成数据。通过合理使用 yield,你可以编写出更高效、更简洁的代码。

二 豆包平台

在 C# 中,yield 关键字主要用于创建迭代器,允许你在迭代器块中使用更简洁的语法来实现自定义迭代器。以下是关于 yield 关键字的详细解释和使用示例:

1. yield return

  • 功能

    • yield return 语句用于在迭代器块中逐个返回元素。当迭代器执行到 yield return 语句时,它会返回一个元素,并保存当前的执行状态,下次调用迭代器时,会从上次暂停的位置继续执行。

示例代码:

using System;
using System.Collections.Generic;

class Program
{
    static IEnumerable<int> GetNumbers()
    {
        yield return 1;
        yield return 2;
        yield return 3;
        yield return 4;
        yield return 5;
    }

    static void Main()
    {
        foreach (var number in GetNumbers())
        {
            Console.WriteLine(number);
        }
    }
}

代码解释:

  • static IEnumerable<int> GetNumbers()

    • 这是一个迭代器方法,返回 IEnumerable<int> 类型,它使用 yield return 逐个返回整数元素。
    • 当调用 GetNumbers() 时,它不会立即执行整个方法,而是返回一个迭代器对象。
    • 每次调用 foreach 迭代时,执行会进入 GetNumbers() 方法,直到遇到 yield return,返回元素并暂停。
    • 下一次迭代会从上次暂停的位置继续,直到方法结束或遇到 yield break

2. yield break

  • 功能

    • yield break 语句用于终止迭代器的执行。

示例代码:

using System;
using System.Collections.Generic;

class Program
{
    static IEnumerable<int> GetNumbers()
    {
        for (int i = 1; i <= 10; i++)
        {
            if (i == 6)
            {
                yield break;
            }
            yield return i;
        }
    }

    static void Main()
    {
        foreach (var number in GetNumbers())
        {
            Console.WriteLine(number);
        }
    }
}

代码解释:

  • GetNumbers() 方法在 i 等于 6 时使用 yield break 终止迭代。
  • 因此,只会输出 1 到 5 的数字。

3. 无限序列:

  • 功能

    • 可以使用 yield return 创建一个无限序列,只要迭代器不使用 yield break 终止,它可以一直生成元素。

示例代码:

using System;
using System.Collections.Generic;

class Program
{
    static IEnumerable<int> InfiniteNumbers()
    {
        int i = 0;
        while (true)
        {
            yield return i++;
        }
    }

    static void Main()
    {
        var iterator = InfiniteNumbers().GetEnumerator();
        for (int i = 0; i < 10; i++)
        {
            iterator.MoveNext();
            Console.WriteLine(iterator.Current);
        }
    }
}

代码解释:

  • InfiniteNumbers() 方法使用 while (true) 创建一个无限序列。
  • 通过 GetEnumerator() 获取迭代器,并使用 MoveNext() 和 Current 手动迭代前 10 个元素。

4. 与 LINQ 结合使用:

  • 功能

    • yield return 可以和 LINQ 结合使用,生成自定义的 LINQ 风格的操作符。

示例代码:

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static IEnumerable<int> FilterEven(IEnumerable<int> numbers)
    {
        foreach (var number in numbers)
        {
            if (number % 2 == 0)
            {
                yield return number;
            }
        }
    }

    static void Main()
    {
        var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        var evenNumbers = FilterEven(numbers);
        foreach (var number in evenNumbers)
        {
            Console.WriteLine(number);
        }
    }
}

代码解释:

  • FilterEven(IEnumerable<int> numbers) 是一个自定义的 LINQ 风格的方法,使用 yield return 筛选偶数。

总结:

  • yield return 可以让你在迭代器方法中暂停执行并返回元素,下次迭代时从暂停处继续。

  • yield break 可以终止迭代器的执行。

  • 可以使用 yield 创建自定义的 LINQ 操作符和无限序列。

通过使用 yield,可以简化迭代器的实现,避免手动实现 IEnumerator<T> 和 IEnumerable<T> 的复杂性,使代码更简洁和易读。

三个人体验

两个平台都很不错,基本上把我想了解的知识点都说清楚了,但是一定要说哪个好一点。针对上面这个案例来说,deepseek平台要比豆包平台好一点点。不仅是展示,还有内容丰富度强一些。这就是我的个人感受。