關(guān)于C#中yield關(guān)鍵字的深入解析
前言
前段時(shí)間了解到y(tǒng)ield關(guān)鍵字,一直覺得還不錯(cuò)。今天給大家分享一下yield關(guān)鍵字的用法。yield return 返回集合不是一次性返回所有集合元素,而是一次調(diào)用返回一個(gè)元素。具體如何使用yield return 返回集合呢?我們一起往下面看吧。
yield使用介紹
yield return 和yield break:
我們看下平常循環(huán)返回集合的使用操作(返回1-100中的偶數(shù)):
class Program
{
static private List<int> _numArray; //用來(lái)保存1-100 這100個(gè)整數(shù)
Program() //構(gòu)造函數(shù)。我們可以通過(guò)這個(gè)構(gòu)造函數(shù)往待測(cè)試集合中存入1-100這100個(gè)測(cè)試數(shù)據(jù)
{
_numArray = new List<int>(); //給集合變量開始在堆內(nèi)存上開內(nèi)存,并且把內(nèi)存首地址交給這個(gè)_numArray變量
for (int i = 1; i <= 100; i++)
{
_numArray.Add(i); //把1到100保存在集合當(dāng)中方便操作
}
}
static void Main(string[] args)
{
new Program();
TestMethod();
}
//測(cè)試求1到100之間的全部偶數(shù)
static public void TestMethod()
{
foreach (var item in GetAllEvenNumberOld())
{
Console.WriteLine(item); //輸出偶數(shù)測(cè)試
}
}
/// <summary>
/// 使用平常返回集合方法
/// </summary>
/// <returns></returns>
static IEnumerable<int> GetAllEvenNumberOld()
{
var listNum = new List<int>();
foreach (int num in _numArray)
{
if (num % 2 == 0) //判斷是不是偶數(shù)
{
listNum.Add(num); //返回當(dāng)前偶數(shù)
}
}
return listNum;
}
}
然后我們?cè)倏纯词褂脃ield return返回集合操作:
class Program
{
static private List<int> _numArray; //用來(lái)保存1-100 這100個(gè)整數(shù)
Program() //構(gòu)造函數(shù)。我們可以通過(guò)這個(gè)構(gòu)造函數(shù)往待測(cè)試集合中存入1-100這100個(gè)測(cè)試數(shù)據(jù)
{
_numArray = new List<int>(); //給集合變量開始在堆內(nèi)存上開內(nèi)存,并且把內(nèi)存首地址交給這個(gè)_numArray變量
for (int i = 1; i <= 100; i++)
{
_numArray.Add(i); //把1到100保存在集合當(dāng)中方便操作
}
}
static void Main(string[] args)
{
new Program();
TestMethod();
}
//測(cè)試求1到100之間的全部偶數(shù)
static public void TestMethod()
{
foreach (var item in GetAllEvenNumber())
{
Console.WriteLine(item); //輸出偶數(shù)測(cè)試
}
}
//使用Yield Return情況下的方法
static IEnumerable<int> GetAllEvenNumber()
{
foreach (int num in _numArray)
{
if (num % 2 == 0) //判斷是不是偶數(shù)
{
yield return num; //返回當(dāng)前偶數(shù)
}
}
yield break; //當(dāng)前集合已經(jīng)遍歷完畢,我們就跳出當(dāng)前函數(shù),其實(shí)你不加也可以
//這個(gè)作用就是提前結(jié)束當(dāng)前函數(shù),就是說(shuō)這個(gè)函數(shù)運(yùn)行完畢了。
}
}
與平常return比較
上面我們看到了yield return 的使用方法,那么這個(gè)與return返回集合有什么區(qū)別呢?我們看下面一個(gè)案例來(lái)進(jìn)行分析:
我們首先先看通過(guò)returun返回集合的一個(gè)案例:
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;
}
}

通過(guò)代碼的運(yùn)行結(jié)果,我們可以看到這里返回的結(jié)果 yield return 和comment return是分成兩邊的。先執(zhí)行完一個(gè)然后開始執(zhí)行另外一個(gè)。不干涉。
我們接著看下使用yield return返回集合:
class Program
{
static void Main(string[] args)
{
foreach (var item in GetNumsYield())
{
Console.WriteLine($" common return:{item}");
}
}
/// <summary>
/// 通過(guò)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;
}
}
}

我們看這個(gè)運(yùn)行結(jié)果,這里yield return 和comment return 的輸出完全交替了。這里說(shuō)明是一次調(diào)用就返回了一個(gè)元素。
通過(guò)上面的案例我們可以發(fā)現(xiàn),yield return 并不是等所有執(zhí)行完了才一次性返回的。而是調(diào)用一次就返回一次結(jié)果的元素。這也就是按需供給。
解析定義類
我們已經(jīng)大致了解了yield 的用法和它與平常的返回的區(qū)別。我們可以繼續(xù)查看其運(yùn)行原理。我們首先看這么一個(gè)案例(在0-10中隨機(jī)返回五個(gè)數(shù)字):
我們通過(guò)SharpLab反編譯其代碼,我們進(jìn)行查看發(fā)現(xiàn)yield具體詳細(xì)實(shí)現(xiàn):


我們看到y(tǒng)ield內(nèi)部含有一個(gè)迭代器。這樣去實(shí)現(xiàn)的迭代遍歷。同時(shí)包含_state字段、用來(lái)存儲(chǔ)上一次的記錄。_current包含當(dāng)前的值、也通過(guò)_initialThreadId獲取當(dāng)前線程id。其中主要的方法是迭代器方法MoveNext()。我們根據(jù)反編譯結(jié)果來(lái)實(shí)現(xiàn)一個(gè)與yiled相似的類:
/// <summary>
/// 解析yield并定義相似類
/// </summary>
public sealed class GetRandomNumbersClass : IEnumerable<int>, IEnumerable, IEnumerator<int>, IDisposable, IEnumerator
{
public static Random r = new Random();
/// <summary>
/// 狀態(tài)
/// </summary>
private int _state;
/// <summary>
///儲(chǔ)存當(dāng)前值
/// </summary>
private int _current;
/// <summary>
/// 線程id
/// </summary>
private int _initialThreadId;
/// <summary>
/// 集合元素?cái)?shù)量
/// </summary>
private int count;
/// <summary>
/// 集合元素?cái)?shù)量
/// </summary>
public int _count;
/// <summary>
/// 當(dāng)前指針
/// </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();
}
}
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
c#一個(gè)定時(shí)重啟的小程序?qū)崿F(xiàn)代碼
今天有個(gè)朋友找我問(wèn)有沒有一些能像Windows一樣計(jì)劃任務(wù)重啟的軟件,我也不清楚。他它說(shuō)能讓我做一個(gè)給他它么?我考慮了一下,他的服務(wù)器都是有安裝.NET框架的,那可以用.NET來(lái)使下~~!2008-09-09
C# 未將對(duì)象引用設(shè)置到對(duì)象的實(shí)例
c#開發(fā)過(guò)程中出現(xiàn)未將對(duì)象引用設(shè)置到對(duì)象的實(shí)例,錯(cuò)誤一般是下面的原因,軟件中也是因?yàn)闆]有獲取到數(shù)據(jù)導(dǎo)致,需要的朋友可以參考下2022-09-09
CPF?使用C#的Native?AOT?發(fā)布程序的詳細(xì)過(guò)程
這篇文章主要介紹了CPF?使用C#的Native?AOT?發(fā)布程序,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具體一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03
C#實(shí)現(xiàn)左截取和右截取字符串實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)左截取和右截取字符串實(shí)例,是針對(duì)字符串的常用操作,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10

