來博客園已經快八個月了,這是第一次發表博客,以前之因此沒有動筆寫,徹底是由於本身功力還太淺薄,喜歡博客園的緣由,就是由於這裏乾淨,純粹,寫下垃圾的博客,供別人閱讀是一件可恥的事。能與博客園結緣是必然的事,我喜歡.net技術,而博客園是國內公認的,關於.net技術網站中最好的一個,在這裏能夠見到衆多MS的MVP,就是最好的證實。「原創纔是博客」的理念在這裏,被你們踐行的很好,這樣的氛圍很適合對技術感興趣的人。八個月前在網上搜索資料時,無心看到了站長嘟嘟大神的佳做,文筆清晰,觀點獨特,細緻入微,讀後讓我欽佩不已;便註冊了一個博客園的帳號。廢話不說了,上碼,若是你能夠一眼看穿下面這段代碼的執行流程,請您就飄過吧(這段代碼摘抄自《C#高級編程》,第七版,讓你們帶着問題來學習我以爲是很好的方式)編程
static void Main() 安全
{ 函數
var game = new GameMoves();學習
var a = game.Cross(); 網站
while (a.MoveNext())ui
{.net
a = a.Current as IEnumerator;設計
} 繼承
Console.Read(); 遞歸
}
public class GameMoves
{
IEnumerator cross;
IEnumerator circle;
public GameMoves()
{
cross = Cross();
circle = Circle();
}
int move = 0;
public const int MaxMoves = 10;
public IEnumerator Cross()
{
while (true)
{
Console.WriteLine("Cross,move{0}", move);
if (++move >= MaxMoves) yield break;
yield return circle;
}
}
public IEnumerator Circle()
{
while (true)
{
Console.WriteLine("Circle,move{0}", move);
if (++move >= MaxMoves) yield break;
yield return cross;
}
}
}
上面的代碼我不會作太多的解釋,若是你理解了本文內容,你應該能夠看懂(注:實在看不懂,就用單步執行吧)。下面再來看看另外這段代碼,當我第一次看到這段代碼的執行結果時,被驚呆了,徹底顛覆了我兩年編程學習對函數執行的認識。
class Program
{
static void Main(string[] args)
{
var ie = IE();
Console.WriteLine("IE()已經調用了,你看到輸出了嗎?"); ie.MoveNext();
Console.Read();
}
public static IEnumerator IE()
{
while (true)
{
Console.WriteLine("你在調用IE()時看不到個人執行,我不是並行方法啊哦\n哈哈,神奇吧,除非你用IE()的返回值調用了MoveNext()\n而且每次調用均可以看到我哦");
yield return "任意類型均可以直接返回哦";
}
}
}
這段代碼稍後我會詳細的解釋,爲了解釋清楚,請耐心聽我慢慢道來,再看一段代碼(哈哈,學編程的,要習慣哦)
static void Main(string[] args)
{
String[] myArr = new String[] { "The", "quick", "brown"};
Console.WriteLine("用標準的C#語法使用枚舉器");
foreach (var str in myArr)
{
Console.WriteLine(str);
}
Console.WriteLine("使用枚舉接口使用枚舉器");
var myEnumerator = myArr.GetEnumerator();
while ((myEnumerator.MoveNext()) && (myEnumerator.Current != null))
{
Console.WriteLine("{0}", myEnumerator.Current);
}
Console.Read();
}
能夠看到,執行結果徹底同樣,編譯器在處理foreach語句塊時,就是編譯爲與以上相似的等價代碼的(對GetEnumerator()的調用);foreach只是語法糖而已;這個方法(GetEnumerator())的返回值是一個接口(IEnumerator),也就是說,在這個方法體內須要出現一個實現了這個接口(System.Collections.IEnumerator)的類。那就先來看看這個接口的定義;
public interface IEnumerator
{
object Current{get;} //注意這個屬性是隻讀的,這也爲何咱們不能在foreach塊中改變集合元素的緣由(集合元素內部的屬性能夠更改哦);
bool MoveNext(); // 將枚舉數推動到集合的下一個元素,成功返回true,失敗返回false
void Reset(); //將枚舉數設置爲其初始位置,該位置位於集合中第一個元素以前
}
這個接口還有一個泛型版本
public interface IEnumerator<T>:IDisposable,IEnumerator
{
T Current{get;};
bool MoveNext();
void Reset();
void Dispose();
}
值得注意的是,泛型版本繼承了IDispose接口,(關於泛型,理解的重點是,要區分開泛型的外殼類和外殼類包含的元素類,這個元素的類型是可變的,而本質上,真正可變的類型確是外殼類自己,這裏的名詞是個人杜撰,還算形象吧)其餘的註釋已經很清楚,再也不過多解釋;(這裏涉及的接口比較多,名稱還很相似,注意分辨哦) 而這個方法(System.Collections.IEnumerator GetEnumerator())自己倒是在IEnumerable接口中定義,能夠看到不少的集合類都繼承實現了這個接口(IEnumerable),而事實上,要使用枚舉器,這個接口並非必須的,只要集合類有一個這個簽名的的方法(System.Collections.IEnumerator GetEnumerator())就足夠了,編譯器根本不會檢查集合類是否實現了接口IEnumberable,這看起來有點神奇,彷佛對代碼安全有點影響,事實上根本沒有,仔細想一下就會明白,編譯器默認會嘗試將foreach轉換爲對 GetEnumerator()的調用,但它發現,類中根本沒有這個方法,天然報錯,注意,報錯不是由於集合沒有實現接口IEnumberable,而是由於集合沒有提供GetEnumerator(),這個我講的已經太羅嗦了,爲了證實這一點,運行下面代碼就能夠證實;注意program類沒有繼承IEnumberable哦,
class Program
{
static void Main(string[] args)
{
Program program=new Program();
foreach (string s in program)
{
Console.WriteLine(s);
}
Console.Read();
}
public IEnumerator GetEnumerator()
{
yield return"一";
yield return "二";
yield return "三";
}
}
下面仔細解釋一下剛纔遺留的代碼(再次貼來,請你把它貼到VS裏執行一下)
class Program
{
static void Main(string[] args)
{
var ie = IE();
Console.WriteLine("IE()已經調用了,你看到輸出了嗎?");
ie.MoveNext();
Console.Read();
}
public static IEnumerator IE()
{
while (true)
{
Console.WriteLine("你在調用IE()時看不到個人執行,我不是並行方法啊哦\n哈哈,神奇吧,除非你用IE()的返回值調用了MoveNext()\n而且每次調用均可以看到我哦");
yield return "任意類型均可以直接返回哦";
}
}
}
至於MS爲何要這樣設計,若是你已經對LINQ有必定的理解,相信你應該能夠理解這一點,LINQ的延遲查詢正是利用這一點。關於LINQ的問題不是這篇博客的重點,反正記住這是MS特殊處理的就行啦。若是要本身實現枚舉器,這個特性也是頗有的。下面咱們再看看這段代碼的另外一個值得關注的地方;yield return語句,能夠看到IE()的返回值是 IEnumerator接口類型,而咱們根本沒有定義實現這個接口的類,那這個方法還怎麼工做啊,應該直接編譯錯誤纔是啊,哈哈,要注意,返回值是經過yield return傳遞的哦,但是yield return裏的類型和返回值類型不同啊,相信不少新手都有這樣的迷惑,其實這是又是語法糖而已,(MS對開發人員真是太好了,爲了咱們少擊打鍵盤,費勁心思啊,但同時對於新手也真是不小的挑戰,容易讓新手浮於技術的表面,而看不到本質的原理)編譯器會把yield return語句轉化爲一個實現了IEnumerator的類,這個類對於編程人員是透明的;每次yield return時都由這個類來收集結果,至於返回值,在調用這個這個方法時,已經當即返回了。能夠把這個這個返回值看作一個特殊的句柄,只有用它調用MoveNext()時(MoveNext()會由foreach語句自動調用哦),該方法才真正執行,固然,yield return部分這時已經再也不方法內了,不然就陷入死遞歸了,感興趣的同窗能夠試試吧yield return返回語句分散開來,中間再夾雜一些其餘語句,而後手工調用Movenext()觀察一下奇怪的執行路線,好了,要介紹的已經差很少了,不明白孩子再好好理解理解吧,否則LINQ就無法學了,本文介紹的內容雖然很基礎,但我認爲對很多人仍是有很多意義的,因此就大膽發到首頁了哈,請大鳥們不要~~