服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - 关于C#中yield关键字的深入解析

关于C#中yield关键字的深入解析

2022-08-09 09:35小世界的野孩子 C#

这篇文章主要给大家介绍了关于C#中yield关键字的深入解析,文中通过示例代码介绍的非常详细,对大家的学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

前段时间了解到yield关键字,一直觉得还不错。今天给大家分享一下yield关键字的用法。yield return 返回集合不是一次性返回所有集合元素,而是一次调用返回一个元素。具体如何使用yield return 返回集合呢?我们一起往下面看吧。

yield使用介绍

yield return 和yield break:

我们看下平常循环返回集合的使用操作(返回1-100中的偶数):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class program
{
static private list<int> _numarray; //用来保存1-100 这100个整数
 
program() //构造函数。我们可以通过这个构造函数往待测试集合中存入1-100这100个测试数据
{
 _numarray = new list<int>(); //给集合变量开始在堆内存上开内存,并且把内存首地址交给这个_numarray变量
 
 for (int i = 1; i <= 100; i++)
 {
 _numarray.add(i); //把1到100保存在集合当中方便操作
 }
}
 
static void main(string[] args)
{
 new program();
 
 testmethod();
 
 
}
 
//测试求1到100之间的全部偶数
static public void testmethod()
{
 foreach (var item in getallevennumberold())
 {
 console.writeline(item); //输出偶数测试
 }
}
 
/// <summary>
/// 使用平常返回集合方法
/// </summary>
/// <returns></returns>
static ienumerable<int> getallevennumberold()
{
 var listnum = new list<int>();
 foreach (int num in _numarray)
 {
 if (num % 2 == 0) //判断是不是偶数
 {
  listnum.add(num); //返回当前偶数
 
 }
 }
 return listnum;
}
}

然后我们再看看使用yield return返回集合操作:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class program
{
static private list<int> _numarray; //用来保存1-100 这100个整数
 
program() //构造函数。我们可以通过这个构造函数往待测试集合中存入1-100这100个测试数据
{
 _numarray = new list<int>(); //给集合变量开始在堆内存上开内存,并且把内存首地址交给这个_numarray变量
 
 for (int i = 1; i <= 100; i++)
 {
 _numarray.add(i); //把1到100保存在集合当中方便操作
 }
}
 
static void main(string[] args)
{
 new program();
 
 testmethod();
 
 
}
 
//测试求1到100之间的全部偶数
static public void testmethod()
{
 foreach (var item in getallevennumber())
 {
 console.writeline(item); //输出偶数测试
 }
}
 
//使用yield return情况下的方法
static ienumerable<int> getallevennumber()
{
 
 foreach (int num in _numarray)
 {
 if (num % 2 == 0) //判断是不是偶数
 {
  yield return num; //返回当前偶数
 
 }
 }
 yield break; //当前集合已经遍历完毕,我们就跳出当前函数,其实你不加也可以
 //这个作用就是提前结束当前函数,就是说这个函数运行完毕了。
}
 
 
}

与平常return比较

上面我们看到了yield return 的使用方法,那么这个与return返回集合有什么区别呢?我们看下面一个案例来进行分析:

我们首先先看通过returun返回集合的一个案例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class program
{
static void main(string[] args)
{
 foreach (var item in getnums())
 {
 console.writeline($" common return:{item}");
 }
}
 
/// <summary>
/// 平常return 返回集合
/// </summary>
/// <returns></returns>
public static ienumerable<int> getnums()
{
 var listnum = new list<int>();
 for (int i = 0; i < 10; i++)
 {
 console.writeline($"yield return:{i}");
 listnum.add(i);
 }
 return listnum;
}
}

关于C#中yield关键字的深入解析

通过代码的运行结果,我们可以看到这里返回的结果 yield return 和comment return是分成两边的。先执行完一个然后开始执行另外一个。不干涉。

我们接着看下使用yield return返回集合:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class program
{
static void main(string[] args)
{
 foreach (var item in getnumsyield())
 {
 console.writeline($" common return:{item}");
 }
}
 
/// <summary>
/// 通过yield return 返回集合
/// </summary>
/// <returns></returns>
public static ienumerable<int> getnumsyield()
{
 for (int i = 0; i < 10; i++)
 {
 console.writeline($"yield return:{i}");
 yield return i;
 }
}
}

关于C#中yield关键字的深入解析

我们看这个运行结果,这里yield return 和comment return 的输出完全交替了。这里说明是一次调用就返回了一个元素。

通过上面的案例我们可以发现,yield return 并不是等所有执行完了才一次性返回的。而是调用一次就返回一次结果的元素。这也就是按需供给。

解析定义类

我们已经大致了解了yield 的用法和它与平常的返回的区别。我们可以继续查看其运行原理。我们首先看这么一个案例(在0-10中随机返回五个数字):

我们通过sharplab反编译其代码,我们进行查看发现yield具体详细实现:

关于C#中yield关键字的深入解析

 关于C#中yield关键字的深入解析

我们看到yield内部含有一个迭代器。这样去实现的迭代遍历。同时包含_state字段、用来存储上一次的记录。_current包含当前的值、也通过_initialthreadid获取当前线程id。其中主要的方法是迭代器方法movenext()。我们根据反编译结果来实现一个与yiled相似的类:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/// <summary>
/// 解析yield并定义相似类
/// </summary>
public sealed class getrandomnumbersclass : ienumerable<int>, ienumerable, ienumerator<int>, idisposable, ienumerator
{
 public static random r = new random();
 
 /// <summary>
 /// 状态
 /// </summary>
 private int _state;
 
 /// <summary>
 ///储存当前值
 /// </summary>
 private int _current;
 
 /// <summary>
 /// 线程id
 /// </summary>
 private int _initialthreadid;
 
 /// <summary>
 /// 集合元素数量
 /// </summary>
 private int count;
 
 /// <summary>
 /// 集合元素数量
 /// </summary>
 public int _count;
 
 /// <summary>
 /// 当前指针
 /// </summary>
 private int i;
 
 int ienumerator<int>.current
 {
  [debuggerhidden]
  get
  {
   return _current;
  }
 }
 
 object ienumerator.current
 {
  [debuggerhidden]
  get
  {
   return _current;
  }
 }
 
 [debuggerhidden]
 public getrandomnumbersclass(int state)
 {
  this._state = state;
  _initialthreadid = environment.currentmanagedthreadid;
 }
 
 [debuggerhidden]
 void idisposable.dispose()
 {
 }
 
 private bool movenext()
 {
  switch (_state)
  {
   default:
    return false;
   case 0:
    _state = -1;
    i = 0;
    break;
   case 1:
    _state = -1;
    i++;
    break;
  }
  if (i < count)
  {
   _current = r.next(10);
   _state = 1;
   return true;
  }
  return false;
 }
 
 bool ienumerator.movenext()
 {
  //ilspy generated this explicit interface implementation from .override directive in movenext
  return this.movenext();
 }
 
 [debuggerhidden]
 void ienumerator.reset()
 {
  throw new notsupportedexception();
 }
 
 [debuggerhidden]
 public ienumerator<int> getenumerator()
 {
  getrandomnumbersclass _getrandom;
  if (_state == -2 && _initialthreadid == environment.currentmanagedthreadid)
  {
   _state = 0;
   _getrandom = this;
  }
  else
  {
   _getrandom = new getrandomnumbersclass(0);
  }
  _getrandom.count = _count;
  return _getrandom;
 }
 
 [debuggerhidden]
 ienumerator ienumerable.getenumerator()
 {
  return getenumerator();
 }
 
 
 [iteratorstatemachine(typeof(getrandomnumbersclass))]
 private static ienumerable<int> getlist(int count)
 {
  getrandomnumbersclass getrandomnumbersclass = new getrandomnumbersclass(-2);
  getrandomnumbersclass._count = count;
  return getrandomnumbersclass;
 }
 private static void main(string[] args)
 {
  ienumerator<int> enumerator = getlist(5).getenumerator();
  try
  {
   foreach (int item in getlist(5))
    console.writeline(item);
   //while (enumerator.movenext())
   //{
   // int current = enumerator.current;
   // console.writeline(current);
   //}
  }
  finally
  {
   if (enumerator != null)
   {
    enumerator.dispose();
   }
  }
  console.readkey();
 }
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对服务器之家的支持。

原文链接:https://www.cnblogs.com/hulizhong/p/11763956.html

延伸 · 阅读

精彩推荐
  • C#浅谈static a[n*m]={0};中static的作用

    浅谈static a[n*m]={0};中static的作用

    下面小编就为大家带来一篇浅谈static a[n*m]={0};中static的作用。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    C#教程网4252021-12-29
  • C#解析C#中断言与异常的应用方式及异常处理的流程控制

    解析C#中断言与异常的应用方式及异常处理的流程控制

    这篇文章主要介绍了C#中断言与异常的应用方式及异常处理的流程控制,一般来说断言用于修正程序员自己的错误而异常用于应对程序运行过程中可能出现的...

    曹宗颖9942021-11-08
  • C#C#语法之泛型的多种应用

    C#语法之泛型的多种应用

    这篇文章主要介绍了C#语法之泛型的多种应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着...

    Kiba51811322022-07-29
  • C#读写XML文件的内容并将其显示在ListView控件上的方法

    读写XML文件的内容并将其显示在ListView控件上的方法

    下面小编就为大家带来一篇读写XML文件的内容并将其显示在ListView控件上的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小...

    C#教程网11522021-12-23
  • C#C#实现listview Group收缩扩展的方法

    C#实现listview Group收缩扩展的方法

    这篇文章主要介绍了C#实现listview Group收缩扩展的方法,结合实例形式分析了listview控件的相关使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下...

    Microblue3712021-11-15
  • C#C#遍历集合与移除元素的方法

    C#遍历集合与移除元素的方法

    这篇文章主要介绍了C#遍历集合与移除元素的方法,结合实例形式分析了C#使用for循环遍历集合以及add与Remove方法进行元素添加与移除的使用技巧,需要的朋友...

    jixiaomeng10812021-11-29
  • C#C#获取串口列表实现实时监控串口

    C#获取串口列表实现实时监控串口

    本文主要介绍两种获取串口列表的方法,比较简单,方便大家使用,另外分享了一个已封装的API,需要的朋友可以参考下。...

    wenjunsu4502021-11-21
  • C#C# 重写ComboBox实现下拉任意组件的方法

    C# 重写ComboBox实现下拉任意组件的方法

    C#种的下拉框ComboBox不支持下拉复选框列表与下拉树形列表等,系统中需要用到的地方使用了第三方组件,现在需要将第三方组件替换掉。这篇文章主要介绍...

    C#教程网7052021-12-08