场景:
假如一个集合中有一百万条数据,我们只想要前1000条。
错误代码示范
foreach (var c in GetCustomers(1000000)){if (c.Id < 1000){Console.WriteLine($"ID:{c.Id},Name:{c.Name}");}else {break;}}Console.Read();static IEnumerable GetCustomers(int count){List listcustomers = new List();for (int i = 0; i < count; i++){var customer = new Customer{Id = i,Name = $"Name{i}"};listcustomers.Add(customer);}return listcustomers;}
这种代码有个很大的错误就是,我们明明只需要1000条数据,它却创建了一百万条数据,极大的浪费了内存。
正确示范,使用yield return
关键字。
foreach (var c in GetCustomersYield(1000000)){if (c.Id < 1000){Console.WriteLine($"ID:{c.Id},Name:{c.Name}");}else {break;}}Console.Read();static IEnumerable GetCustomersYield(int count) {for (int i = 0; i < count; i++){yield return new Customer { Id=i,Name=$"Name{i}"};}}
通过运行代码可以看到,GetCustomersYield(1000000)
不会立刻去创建实体,甚至代码不会直接运行GetCustomersYield
这个方法,他会先走循环,然后根据循环体需求,去创建数据。yield return
底层是对迭代器的实现。所以方法可以直接返回IEnumerable
类型,除非数据必须使用,不然yield return
返回的对象是不会被立即创建的,有点懒加载的意思。实际上yield return
返回的并不是数据,而是数据的迭代。通过多次叠加,其实循环遍历的结果,就是迭代器的集合。这样使用yield return
关键字就只创建出来了1000条数据,大大的节省了内存开销。
接下来我们对 上面的2个代码 示例做下性能对比 。
Nuget 下载BenchmarkDotNet
跑分工具。
单独创建一个BenchmarkTest
类去跑
[MemoryDiagnoser]public class BenchmarkTest{[Benchmark]public void ProcessCustomer() {var customers = GetCustomers(1000000);foreach (var c in customers){if (c.Id < 1000){Console.WriteLine($"ID:{c.Id},Name:{c.Name}");}else{break;}}}[Benchmark]public void ProcessCustomerYield(){var customers = GetCustomersYield(1000000);foreach (var c in customers){if (c.Id < 1000){Console.WriteLine($"ID:{c.Id},Name:{c.Name}");}else{break;}}}static IEnumerable GetCustomers(int count){List listcustomers = new List();for (int i = 0; i < count; i++){var customer = new Customer{Id = i,Name = $"Name{i}"};listcustomers.Add(customer);}return listcustomers;}static IEnumerable GetCustomersYield(int count){for (int i = 0; i < count; i++){yield return new Customer{Id = i,Name = $"Name{i}"};}}}
切记类上和方法上要加上对应的特性。
主方法调用
static void Main(string[] args){var sumery = BenchmarkRunner.Run();}
Benchmark只能在Release
版本进行跑分,打开项目根目录。然后文件夹上 cmd
输入如下命令生成Release
dotnet build -c Release
拿到画线的地址,执行如下命令
dotnet D:\test\集合操作\集合操作\bin\Release\net5.0\集合操作.dll
等待跑分结果,如图:
C#课程欢迎留言或者私聊。