概要
Take方法作為IEnumerable的擴(kuò)展方法,具體對(duì)應(yīng)兩個(gè)重載方法。本文主要分析第一個(gè)接收整數(shù)參數(shù)的重載方法。
源碼解析
Take方法的基本定義
public static System.Collections.Generic.IEnumerable Take (this System.Collections.Generic.IEnumerable source, int count);
基本功能是從序列source中,返回指定個(gè)數(shù)count的相鄰元素。
源碼分析
Take.cs
public static IEnumerable<TSource> Take<TSource>(this IEnumerable<TSource> source, int count)
{
if (source == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
return count <= 0 ?
Empty<TSource>() :
TakeIterator<TSource>(source, count);
}
Take方法本身代碼很簡(jiǎn)單, 首先作了一個(gè)空序列的檢查,如果序列為空,則拋出異常。然后如果count是0,即取前0項(xiàng)相鄰元素,等價(jià)于什么也不作,直接返回,否則調(diào)用TakeIterator方法。
Take.SizeOpt.cs
private static IEnumerable<TSource> TakeIterator<TSource>(IEnumerable<TSource> source, int count)
{
Debug.Assert(count > 0);
foreach (TSource element in source)
{
yield return element;
if (--count == 0) break;
}
}
TakeIterator方法并沒(méi)有像我們之前分析的Where,Select等方法那樣,根據(jù)功能,定于很多Iterator的派生類(lèi)來(lái)實(shí)現(xiàn)具體的功能,而是使用了yield return的方式。
按照count的個(gè)數(shù)取出對(duì)應(yīng)的元素,以yield return的方式返回。
下面我們使用相同的代碼,定義我們自己的擴(kuò)展方法take 和takeIterator,通過(guò)log來(lái)搞清楚yield return方式的實(shí)現(xiàn)細(xì)節(jié)。
public static IEnumerable<TSource> take<TSource>(this IEnumerable<TSource> source, int count)
{
Console.WriteLine("take is called !");
if (source == null)
{ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
return count <= 0 ?
Empty<TSource>() :
takeIterator<TSource>(source, count);
}
private static IEnumerable<TSource> takeIterator<TSource>(IEnumerable<TSource> source, int count)
{
Console.WriteLine("TakeIterator is called !");
Debug.Assert(count > 0);
foreach (TSource element in source)
{
Console.WriteLine("Enter takeIterator Foreach");
yield return element;
Console.WriteLine("Return " + element + " from takeIterator Foreach");
if (--count == 0) break;
}
}
Case 1 不通過(guò)toList或foreach循環(huán)來(lái)調(diào)用take的返回值。
static void Main(string[] args)
{
var list = Enumerable.Range(1,10).take(2);
}
執(zhí)行結(jié)果如下:
我們可以看到takeIterator并未被調(diào)用。
Case 2: 通過(guò)foreach循環(huán)來(lái)調(diào)用take的返回值
static void Main(string[] args)
{
var list = Enumerable.Range(1,10).take(2);
foreach (var item in list)
{
Console.WriteLine("Enter foreach Main function's foreach");
Console.WriteLine("Print " + item + " in Main function");
}
}
執(zhí)行結(jié)果如下:
從執(zhí)行結(jié)果可以看出:
- takeIterator函數(shù)只執(zhí)行一次,但是會(huì)生成一個(gè)狀態(tài)機(jī),用于返回take出來(lái)的所有數(shù)據(jù);
- Main函數(shù)中的foreach每次的取值,是從狀態(tài)機(jī)中獲取數(shù)據(jù),即通過(guò)yield return的方式獲取。
結(jié)論
通過(guò)定義具體迭代器實(shí)現(xiàn)的延遲加載和通過(guò)yield return方式實(shí)現(xiàn)的延遲加載,本質(zhì)上沒(méi)有區(qū)別。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-641624.html
但是實(shí)現(xiàn)上略有不同,定義迭代器方式實(shí)現(xiàn)的Where或Select等方法,如果沒(méi)有取值操作,它只是將迭代器對(duì)象返回,迭代器對(duì)象中保存了迭代方式和源數(shù)據(jù)序列,對(duì)應(yīng)的方法會(huì)被調(diào)用。通過(guò)yield return方式實(shí)現(xiàn)的迭代器,如果沒(méi)有取值操作,yield return所在的方法不會(huì)被調(diào)用。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-641624.html
到了這里,關(guān)于C# Linq源碼分析之Take方法的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!