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

通過代碼的運行結(jié)果,我們可以看到這里返回的結(jié)果 yield return 和comment return是分成兩邊的。先執(zhí)行完一個然后開始執(zhí)行另外一個。不干涉。
我們接著看下使用yield return返回集合:
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;
}
}
}

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


我們看到y(tǒng)ield內(nèi)部含有一個迭代器。這樣去實現(xiàn)的迭代遍歷。同時包含_state字段、用來存儲上一次的記錄。_current包含當(dāng)前的值、也通過_initialThreadId獲取當(dāng)前線程id。其中主要的方法是迭代器方法MoveNext()。我們根據(jù)反編譯結(jié)果來實現(xiàn)一個與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>
///儲存當(dāng)前值
/// </summary>
private int _current;
/// <summary>
/// 線程id
/// </summary>
private int _initialThreadId;
/// <summary>
/// 集合元素數(shù)量
/// </summary>
private int count;
/// <summary>
/// 集合元素數(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é)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
相關(guān)文章
CPF?使用C#的Native?AOT?發(fā)布程序的詳細(xì)過程
這篇文章主要介紹了CPF?使用C#的Native?AOT?發(fā)布程序,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具體一定的參考借鑒價值,需要的朋友可以參考下2022-03-03

