2013年10月26日 星期六

Enum 列舉型別 介紹

Enum 關鍵字用來宣告列舉型別 (Enumeration),是由一組稱為列舉值清單的具名常數所構成的獨特型別。
通常最好是在命名空間內直接定義 enum,讓該命名空間中的所有類別都能同樣便利地存取它。不過,enum 也可以透過巢狀方式置於個類別或結構 (Struct) 中。
根據預設,第一個列舉值的值是 0,而每一個接下來的列舉值會遞增 1

來看一下範例


這邊第一個列舉值意思是Black 這個預設值是0開始 依此類推1,2,3,4~11的None

當然這個順序,不一定要照系統預設的 也可以自己改,看下面程式碼!!








這邊可以看到 Sat=6 以此類推 Sum 就一定會是7,除非你在Sum=後面加上常數不然會依
前面列舉值+1
再來看 Mon=1,以此類推 Tur=2 一子到 Fri 當然會等於5了唷!


每個列舉型別都有基礎型別,此基礎型別可以是除了 char 型別以外的任何整數型別。列舉項目的預設基礎型別是 int。若要宣告另一個整數類資料型別 (Integral Type) 的 enum,例如 byte,請在後面接著型別的識別項之後使用冒號:
來看下面程式碼


 

enum Days : 後面加上型別就行了
enum 的核准型別為 bytesbyteshortushortintuintlongulong
可將基礎型別範圍內的任何值指派給 Days 型別的變數;這些值並不受限於具名常數。


再來我們來看看 如何使用吧!!
分別是以值取字
或是以字取值

看下圖!!



 

大概介紹到這!! 日後補充

2013年10月24日 星期四

替換字串的方法 String.Replace()

假如今天打了一篇日記但手殘打錯了故事中主角的名子了
要修改!卻要把整篇文章重頭到尾打錯的名子
一個一個的改回來 很麻煩!!

這時候有個方便又好用的方法

假如故事如下

豬爸爸生豬兒子,豬兒子愛把妹

把豬改成帥哥!

可以使用String.Replace() 語法如下
public string Replace(string oldValue, string newValue)

oldValue
型別:System.String
要被取代的字串。
newValue
型別:System.String
用來取代所有出現之 oldValue 的字串。
 
 
來看實例程式碼如下


 

 

分割字串Split

這裡分割的意思是
比如說有一個字串是
我是@豬與帥哥$的重合體^哈哈哈
我要把@$^給分割掉 這時候用Split這方法超好用
來看一下MSDN的語法,有如下幾種
String.Split 方法




來看第一個程式範例
語法如下public string[] Split(params char[] separator)















不多說,圖片簡單又明瞭!
注意的是
separator 的每個項目各定義一個分隔字元
就是一個字串陣列一個Empty, 那麼如果兩個分隔符號為相鄰
如下圖也就是兩個^就會多產生一個Empty
或是在此執行個體的開頭或結尾 找到一個分隔符號
則對應的陣列元素會包含 Empty
因為每個項目前跟後各有一個分割字元
其餘多的將會被推入空陣列     

請看下圖:因為兩個^造成兩個分割字元
一個是要給 "我是帥哥"的 於是 第二個空字串則推入陣列
 

        











要解決這問題也很簡單
給他一個foreach 跟一個if 判斷不等於""空字串時印出!



















而這裡還搭配一個Trim()方法用於清除空白字串
看下圖






我們也可以使用String.Split 方法 (Char[], StringSplitOptions)
來達到不回傳空字串的目的
語法如下
[ComVisibleAttribute(false)]
public string[] Split(char[] separator,StringSplitOptions options)
來看程式碼如下










啊在假如 今天判斷式改成string
可以用這個語法 如下

String.Split 方法 (String[], StringSplitOptions)

來看圖片吧!











不一樣的是 Scut 是一個字串唷!!


再來看最後一個例子
語法如下
String.Split 方法 (Char[], Int32)
或者
String.Split 方法 (String[], Int32, StringSplitOptions)

可以看到 第二個參數是一個 Int32 整數型態
用來限制回傳字串陣列的最大數目
如果是2 代表只有兩個陣列 0跟1
如果是3 代表只有三個陣列0,1,2
來看下圖 就能明白!!









看紅色箭頭!! 可以注意的是2
分別得到兩個陣列

再來看下圖 將參數改成3 出現三個陣列
因此可以清楚明白他的規則

2013年10月23日 星期三

利用(基礎)REGEX正規化查詢字串中有幾個中文與數字

利用簡易的正規化來看字串中有幾個中文字
首先本篇將會介紹 簡易的正規化如何使用
語法如下
Regex 物件名 = new Regex("pattern")
在pattern放入符號表達式
下面圖表來源為菲力貓的程式設計


符號
表示式
說明或範例
不成立字串
.
a.
表示任何字元。
含字母 "a" 以及其後任一個字元的字串。
Ex: "ab", "bac"
"a", "ba"
重覆字串
*
ab*
表示沒有或更多字元。
"ab*":表示一個字串有一個a後面跟著零個或若干個b
Ex: "a", "ab", "abbb"
+
ab+
一次或更多字元。
"ab+":表示一個字串有一個a後面跟著至少一個b或者更多
Ex: “ab”,”abbbb”
?
ab?
沒有或一次字元。
ab?":表示一個字串有一個a後面跟著零個或者一個b
Ex: “a”,”ab”
[]
[13579]
方括號表示某些字元允許在一個字串中的某一特定位置出現。
包含 "1" "3" "5" "7" "9" 的字串。
Ex: "a3b", "1xy"
"y2k"
[0-9]
含數字之字串
不含數字之字串
[a-z0-9]
含數字或小寫字母之字串
不含數字及小寫字母之字串
[a-zA-Z0-9]
含數字或字母之字串
不含數字及字母之字串
b[aeiou]t
"bat", "bet", "bit", "bot", "but"
"bxt", "bzt"
^[a-zA-Z]
表示一個以字母開頭的字串
[0-9]%
表示一個百分號前有一位元的數位
[a-zA-Z0-9]$
表示一個字串以一個逗號後面跟著一個字母或數位結束
{}
ab{2}
用以表示重復次數的範圍。
表示一個字串有一個a跟著2b
Ex: "abb"
ab{2,}
表示一個字串有一個a跟著至少2b
ab{3,5}
表示一個字串有一個a跟著35b
字串位置
^
^xy
"xy" 開始的字串
Ex: "xyz", "xyab"
"axy", "bxy"
$
xy$
"xy" 結尾的字串
Ex: 例如:"axy", "abxy"
"xya", "xyb"
[^]
[^0-9]
不希望出現的字元,'^'應在方括號裏的第一位。
不含數字之字串
含數字之字串
%[^a-zA-Z]%
兩個百分號中不應該出現字母
[^aeiouAEIOU]
不含母音之字串
含母音之字串
[^\^]
不含 "^" 之字串,例如:"xyz", "abc"
"xy^", "a^bc"
特殊字元
\d
數字0~9
\D
非數字
\w
數字、字母、底線
\W
\w
\s
空白字元 + \r\t\n\f
\S
\s
|
(b¦cd)ef
表示”或”
"bef""cdef"
\
^\^
跳脫字元,將特殊符號的義意去除,^.$()¦*+?{\"這些字元前加上跳脫字元'\'
字首必須是^

 








 


程式碼如下!!

private void btn_GetCount_Click(object sender, EventArgs e)
        {
           
            int P_scalar = 0;//定義值類型變數並賦值為0
            Regex P_regex = //建立正則表達式對象,用於判斷字符是否為中文字
                new Regex("^[\u4E00-\u9FA5]{0,}$");
            for (int i = 0; i < txt_str.Text.Length; i++)//深度搜尋字串中每一個字符
            {
                P_scalar = //如果檢查的字符是中文字則計數器加1沒有則不動作
                    P_regex.IsMatch(txt_str.Text[i].
                    ToString()) ? ++P_scalar : P_scalar;
            }
            txt_count.Text = P_scalar.ToString();//顯示中文字數量
        }


那如果要查詢英文字母呢??  一樣
跟上面差不多 唯一修改的地方是

private void button2_Click(object sender, EventArgs e)
        {
            int COUNT = 0;
            Regex tr = new Regex("^[A-Za-z]");
            for (int w = 0; w < txt_str.Text.Length; w++)
            {
                COUNT = tr.IsMatch(txt_str.Text[w].ToString()) ? ++COUNT : COUNT;
            }
            label3.Text = COUNT.ToString();
        }




顯示數字可以用正規化
不過下面不用正規劃
改用一下 char.IsDigit如果是數字會回傳一個BOOL


private void button1_Click(object sender, EventArgs e)
        {
            int r = 0;//計數器
            char[] MyisDigit = txt_str.Text.ToCharArray();//字轉轉字元陣列
            foreach (char E in MyisDigit)//一個一個搜尋
            {
                while (char.IsDigit(E))//如果是數字則回傳true
                {
                    r = r + 1;
                    break;//跳出while迴圈 不然會永無止境的循環
                }
               
            }
            //將累加結果SHOW出
            MessageBox.Show("字串中有" + r.ToString() + "個數字");
        }

2013年10月20日 星期日

Substring取字串指定字元,LastIndexOf取字元索引

本章重點於 Substring與LastIndexOf


        private void button1_Click(object sender, EventArgs e)
        {
            string teST = "ABCDEFG";//範例
            //這裡不用string 改用一下StringBuilder
            StringBuilder tr = new StringBuilder();
            //使用Substring SHOW出起始0開始取2個字元 等於"AB"
            tr.Append(teST.Substring(0, 2));           
            MessageBox.Show(tr.ToString());//show出
            tr.Clear();// 清空字串
            //查詢字串中,最後一個D索引數是多少 0為起始 所以是3
            //最後一個索引,意思是假如"ABCDA" 他會找到最右邊的A
            //所以是4
            tr.Append(teST.LastIndexOf("D"));
            MessageBox.Show(tr.ToString());//SHOW
        }


PS:如果LastIndexOf找不到對應字元會 回傳-1


再來看檔案範例
如何顯示檔案路徑中的檔案名稱與副檔名呢
來看範例 使用到了penFileDialog1控制項

private void btn_Openfile_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)//判斷是否選擇了檔案
            {
                string P_str_all = openFileDialog1.FileName;//記錄選擇的檔案全路徑
                //取得檔案路徑
                //為何要+1  因為Substring將印出索引0~"\\"的字串
                //而Substring(,)第二個參數是取出字元數 IndexOf索引是0開始
                //所以P_str_all.LastIndexOf("\\") + 1才不會漏掉一個
                string P_str_path = P_str_all.Substring(0, P_str_all.LastIndexOf("\\") + 1);
                //取得檔案名
                //假如是C:\Users\Jian\XXX.TXT好了
                //Substring第一個參數是最後的"\\"+1 索引第14 在後面就是檔名了
                //Substring第二個參數則是副檔名前面的.(17)減(14)=3
                //所以是Substring(14,3) 剛好等於XXX的部分
                string P_str_filename = P_str_all.Substring(P_str_all.LastIndexOf("\\") + 1,
                    P_str_all.LastIndexOf(".") -(P_str_all.LastIndexOf("\\") + 1));
                string P_str_fileexc = //取得檔案副檔名 方法同上
                    P_str_all.Substring(P_str_all.LastIndexOf(".") + 1,
                    P_str_all.Length - P_str_all.LastIndexOf(".") - 1);
                lb_filepath.Text = "檔案路徑: " + P_str_path;//顯示檔案路徑
                lb_filename.Text = "檔案名稱: " + P_str_filename;//顯示檔案名
                lb_fileexc.Text = "檔案副檔名: " + P_str_fileexc;//顯示副檔名
            }
        }

完畢!!

刪除字串中的空白,使用了IEnumeraor,toCharArray,三元運算

本章運用了前幾張所介紹本章將會使用到的技巧
如果忘記的話可以看看之前所寫的筆記
關於 IEunmerable IEnumerator,ToCharArray,StringBuilder

先來看程式範例
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
namespace RemoveBlank
{
    public partial class Frm_Main : Form
    {
        public Frm_Main()
        {
            InitializeComponent();
        }
        private void btn_RemoveBlank_Click(object sender, EventArgs e)
        {
            //先將再txt_str.Text輸入的中文轉乘字元陣列
            char[] P_chr = txt_str.Text.ToCharArray();//得到字符陣列
            //P_chr.GetEnumerator() 得到列舉器
            IEnumerator P_ienumerator_chr = P_chr.GetEnumerator();
            //建立stringbuilder對像
            StringBuilder P_stringbuilder = new StringBuilder();
            //將列舉值往前推至下集合中的下一個項目。如果成功則返回為 true
            //如果超過集合結尾,則返回false。
            while (P_ienumerator_chr.MoveNext())//開始列舉
            {
                //向stringbuilder對像中Append新增非空格字符
                //並且P_ienumerator_chr.Current使用Current取矩陣中的每一個字元
                //如果不等於" " 空白 =true 貼上目前矩陣自原P_ienumerator_chr.Current.ToString()
                //false 貼上空的字串string.Empty
                P_stringbuilder.Append(
                    (char)P_ienumerator_chr.Current != ' ' ?
                    P_ienumerator_chr.Current.ToString() : string.Empty);
            }
            //印出P_stringbuilder 得到沒有空格的字串
            txt_removeblank.Text = P_stringbuilder.ToString(); 
        }
    }
}
1

雖然目前看來都能一目了覽
但為了怕忘記 還是大改講解一下
首先 我們將會在 txt_str輸入字串+空白
而後txt_removeblank將會輸出去除空白的字串

首先我們利用ToCharArray()將txt_str字串,以陣列方式
存取Char[] 名子叫P_chr
然後再對P_chr.GetEnumerator 取得列舉
將會把列舉回傳給IEnumerator介面

再來實體化一個StringBuilder類別,先放著等等要存取結果用

再來一個while迴圈裡面搭配IEnumerator.MoveNext()
MoveNext()會查看列舉是否可往前推送一個索引 如果是為TRUE
將會運行{}下的程式碼,並以StringBuilder.Append存取字串
在存取字串區塊中,我們使用了三元運算來判斷
本陣列索引使否是一個" "空白鍵,如果不是
將回傳true 三元運算後面將會回傳前者
P_ienumerator_chr.Current.ToString()
傳回本次索引值內的字串

那麼如果是false則傳回後者string.Empty空字串
PS:空字串是什麼都沒有"",空白鍵是" "

最後我們將StringBuilder.Tostring回傳給txt_removeblank.Text
並且印出 就大功告成了!

完畢!


2013年10月19日 星期六

IEnumerable 與 IEnumerator 介面如何實作foreach

前章 認識IL程式碼
在觀察Foreach中的IL我們發現了 Foreach中使用了
IEnumerable 與 IEnumerator 介面 本章將介紹
foreach中如何調用這兩種介面

OK! 首先 我們先來 談談 IEnumerable
可以看到此介面 只提供一個方法GetEnumerator
此方法會 傳回會逐一查看集合的列舉程式。

這邊要注意的是IEnumerator .GetEnumerator() 此方法
是回傳IEnumerator 而不是 IEnumerable
所以  IEnumerable只提供了 列舉的功能
其他的事情 將交給IEnumerator

再來 來看IEnumerator介面

假如 我們在之前的介面 已使用GetEnumerator() 取的列舉後
將列舉值傳到此介面 繼續後續動作
先來看看 IEnumerator 提供了 哪些方法??

bool MoveNext()
如果列舉值成功地前移至下一個項目,則為 true
如果列舉值已超過集合的結尾,則為 false
意思是說 他會有一個   參數++ >索引.Length
不斷累加 一子到超出索引範圍 才會=false

Reset 方法
設定列舉值至它的初始位置,這是在集合中第一個元素之前。
用於初始化 參數 ,這裡參數 同MoveNext()中累加的參數是同一個

Current 屬性
取得集合中目前的項目。
當MoveNext true時,將集合[參數值] 放入結果中

介紹完Enumeraor Enumerable
簡單來說 我們必須 使用 使用GetEnumerator()
取得矩陣後 再利用Enumerator介面所提供的方法

下面將實作GetEnumerator方法 讓我們能了解foreach中的運作原理
好的 首先 來看一下例題

首先 我們建立一個CS檔案內部實作類別如下

class MyCollection
    {
        private int[] item; //宣告 私用 整數 陣列叫 item,尚未初始化參數
        public MyCollection()
        {
            this.item = new int[5] {12, 44, 33, 2, 50};//初始化 剛剛宣告的 陣列
           
        }
        public MyEnumerator GetEnumerator()//foreach將會呼叫GetEnumerator()
        {                                  //並回傳MyEnumerator類別
            return new MyEnumerator(this);//將會實體化MyEnumerator並帶入(item)
        }
        public class MyEnumerator
        {
            int nIndex;
            MyCollection collection;
            public MyEnumerator(MyCollection coll)
            {
                ////當GetEnumerator()被呼叫 同時完成此區塊
                collection = coll;
                nIndex = -1;
            }
            public bool MoveNext()
            {
                nIndex++;
                return (nIndex < collection.item.GetLength(0));
            }
            public int Current
            {
                get
                { return (collection.item[nIndex]); }
            }
        }
    }


接下來 來看 主程式 呼叫副程式 副程式實作foreach
foreach將會 呼叫GetEnumerator()

程式碼如下
class Program
    {
        static void Sample_1()
        {
            Console.WriteLine("*-*-*-*-*-*-範例1 開始。"); Console.ReadLine();
            MyCollection col = new MyCollection();//實體化類別 並同事建立item陣列
            Console.WriteLine("集合中的值為:");
            // 顯示集合中的項目
            foreach (int i in col)// 逐步執行當執行到col時 foreach會呼叫GetEnumerator()
            {
                Console.WriteLine(i);
            }
            Console.WriteLine("*-*-*-*-*-*-範例1 結束。"); Console.ReadLine();
        }
         static void Main(string[] args)//主程式
        {
            Sample_1();//呼叫副程式
         }
    }




OK! 看完程式碼 想必 第一次 應該很亂吧!
讓我大概解析一下 他的步驟

首先 Program.cs 程式進入開端 main
裡面有一個sample_1();
他將會呼叫同 Program.cs程式檔中的類別
裡面的靜態成員static void Sample_1()
而這裡面 將會實體化 MyCollection類別
並且初始化類別內部item陣列

當實體化MyCollection col=new MyCollection
類別完後 將執行foreach中col的區塊
此時foreach,將會調用GetEnumerator()公開方法
此時 GetEnumerator()返回的是一個類中類MyEnumerator
GetEnumerator() 裡面將實體化new MyEnumerator(item)

程序便跳到 類中類MyEnumerator 裡面支援一個方法
MyEnumerator(MyCollection coll)
這方法中 將nindex 與collection 做初始化動作
待會這些初始化的參數 將被public bool MoveNext()調用
 
結束GetEnumerator()內部程序後 也初始化了MyEnumerator中的參數
並將 列舉數值,同樣返回MyEnumerator

這時候 foreach中col 呼叫GetEnumerator的工作已結束

接下來 將執行foreach中的in
此時 foreach 將呼叫bool MoveNext
此方法 返回是一個布林
取決於 剛剛初始化的nIndex是否小於列舉.length
此時nIndex++ 會不斷累加 一子到 陣列終點
MoveNext執行完後 回到foreach 中的in

如果之前MoveNext=false
挑出foreach迴圈 如果是 true
會接下去 執行foreach中的 int i 區塊

foreach將調用public int Current
他將回傳給foreach中的i
來看看Current內部程序 如何回傳
Get
   { return (collection.item[nIndex]); }
很清楚明白的 調用類別Collection中的item[]

之後i再回傳給 console.writeline(i)
結束!!

接下來我們來大概 看一下內部IL程式碼長啥樣?

.method private hidebysig static void  Sample_1() cil managed
{
  // Code size       118 (0x76)
  .maxstack  2
  .locals init ([0] class _002_foreach_test.nTest.MyCollection col,
           [1] int32 i,
           [2] class _002_foreach_test.nTest.MyCollection/MyEnumerator CS$5$0000,
           [3] bool CS$4$0001,
           [4] class [mscorlib]System.IDisposable CS$0$0002)
//上面宣告了4個V0=MyCollection col 類別
//           V1=foreach中的i
//           V2=類中類MyEnumerator
//           V3=bool 這是MoveNExt
//           V4=IDisposable清除資源用

  IL_0000:  nop
  IL_0001:  ldstr      bytearray (2A 00 2D 00 2A 00 2D 00 2A 00 2D 00 2A 00 2D 00   // *.-.*.-.*.-.*.-.
                                  2A 00 2D 00 2A 00 2D 00 C4 7B 8B 4F 31 00 20 00   // *.-.*.-..{.O1. .
                                  8B 95 CB 59 02 30 )                               // ...Y.0

//0001這裡是一個中文與符號的unicode代碼 將被下面console show出
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  call       string [mscorlib]System.Console::ReadLine()
  IL_0011:  pop

  IL_0012:  newobj     instance void _002_foreach_test.nTest.MyCollection::.ctor()//實體化類別
  IL_0017:  stloc.0 //並存取col

  IL_0018:  ldstr      bytearray (C6 96 08 54 2D 4E 84 76 3C 50 BA 70 1A FF )       // ...T-N.v<P.p..
  IL_001d:  call       void [mscorlib]System.Console::WriteLine(string)
//0018是一個unicode 將被001d SHOW出

  IL_0022:  nop
  IL_0023:  nop
  IL_0024:  ldloc.0//讀取V0推入堆疊
  IL_0025:  callvirt   instance class _002_foreach_test.nTest.MyCollection/MyEnumerator _002_foreach_test.nTest.MyCollection::GetEnumerator()
//0025呼叫GetEnumerator()去實作內部區塊
//而這區塊是指向類中類的MyEnumerator
  IL_002a:  stloc.2//這邊V2也存一份列舉值
  .try
  {
    IL_002b:  br.s       IL_003d
    IL_002d:  ldloc.2 //讀取列舉
    IL_002e:  callvirt   instance int32 _002_foreach_test.nTest.MyCollection/MyEnumerator::get_Current()
    IL_0033:  stloc.1//將列舉[]放入V1
    IL_0034:  nop
    IL_0035:  ldloc.1//讀取V1將被下面0036SHOW出
    IL_0036:  call       void [mscorlib]System.Console::WriteLine(int32)
              //此後以此類推如法炮製
    IL_003b:  nop
    IL_003c:  nop

    IL_003d:  ldloc.2// 讀取剛剛存取的列舉值
    IL_003e:  callvirt   instance bool _002_foreach_test.nTest.MyCollection/MyEnumerator::MoveNext()
    IL_0043:  stloc.3//將MoveNext結果放入V3
    IL_0044:  ldloc.3//讀取V3
    IL_0045:  brtrue.s   IL_002d//true就跳到002d
    IL_0047:  leave.s    IL_0063//不然就跳出foreach迴圈
  }  // end .try
  finally
  {
    IL_0049:  ldloc.2
    IL_004a:  isinst     [mscorlib]System.IDisposable
    IL_004f:  stloc.s    CS$0$0002
    IL_0051:  ldloc.s    CS$0$0002
    IL_0053:  ldnull
    IL_0054:  ceq
    IL_0056:  stloc.3
    IL_0057:  ldloc.3
    IL_0058:  brtrue.s   IL_0062
    IL_005a:  ldloc.s    CS$0$0002
    IL_005c:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0061:  nop
    IL_0062:  endfinally
  }  // end handler
  IL_0063:  nop
  IL_0064:  ldstr      bytearray (2A 00 2D 00 2A 00 2D 00 2A 00 2D 00 2A 00 2D 00   // *.-.*.-.*.-.*.-.
                                  2A 00 2D 00 2A 00 2D 00 C4 7B 8B 4F 31 00 20 00   // *.-.*.-..{.O1. .
                                  50 7D 5F 67 02 30 )                               // P}_g.0
  IL_0069:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_006e:  nop
  IL_006f:  call       string [mscorlib]System.Console::ReadLine()
  IL_0074:  pop
  IL_0075:  ret
} // end of method Program::Sample_1

以上是一個 簡單的例子 我們在MYcollection.cs
中沒使用到IEnumerator IEnumerable的
命名空間using System.Collections;
IL程式碼中可以證明!! 並無調用到Collections

接下來我們來看更進階的實做~~
這次實作interface 中的IEnumerator IEunmerable


一樣Main 中 Sample_2_a();呼叫副程式如下
static void Sample_2_a()
        {
            Console.WriteLine("*-*-*-*-*-*-範例2_a 開始。"); Console.ReadLine();
            MyCollection2_a col = new MyCollection2_a();
            Console.WriteLine("集合中的值為:");
            // 顯示集合中的項目
            foreach (int i in col)
            {
                Console.WriteLine(i);
            }
            Console.WriteLine("*-*-*-*-*-*-範例2_a 結束。"); Console.ReadLine();
        }

來看一下副程式所呼叫的類別CS程式檔中的類別如下
此類使用命名空間 collections

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace _002_foreach_test.nTest
{
    // 宣告這個 collection 實做 IEnumerable 介面
    class MyCollection2_a : IEnumerable
    {
        private int[] items;
        public MyCollection2_a()
        {
            this.items = new int[5] { 12, 44, 33, 2, 50 };
        }
        public MyEnumerator2_a GetEnumrator()
        {
            return new MyEnumerator2_a(this);//實體化yEnumerator2_a(item)
                                             //並且傳回列舉
        }
        // 實做 GetEnumerator()
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumrator();//呼叫自己的方法
            //等待回傳列舉給IEnumerator
        }
        // 宣告這個 enumerator 實做 IEnumerator 介面
        public class MyEnumerator2_a : IEnumerator
        {
            private int nIndex;
            MyCollection2_a collection;
            public MyEnumerator2_a(MyCollection2_a coll)
            {
                this.collection = coll;//當呼叫GetEnumerator()時
                this.nIndex = -1;      //此方法中的參數被初始化
            }
            #region IEnumerator 成員
            object IEnumerator.Current
            {
                get { return (this.collection.items[nIndex]); }
            }
            bool IEnumerator.MoveNext()
            {
                this.nIndex++;
                return (nIndex < collection.items.GetLength(0));
            }
            void IEnumerator.Reset()
            {
                this.nIndex = -1;
            }
            #endregion
        }
    }
}

 跟上一個範例差不多 不同於實作interface

來看一下IL 證明這次有使用到此命名空間與實作了interface
.method private hidebysig static void  Sample_2_a() cil managed
{
  // Code size       123 (0x7b)
  .maxstack  2
  .locals init ([0] class _002_foreach_test.nTest.MyCollection2_a col,
           [1] int32 i,
           [2] class [mscorlib]System.Collections.IEnumerator CS$5$0000,
           [3] bool CS$4$0001,
           [4] class [mscorlib]System.IDisposable CS$0$0002)
  IL_0000:  nop
  IL_0001:  ldstr      bytearray (2A 00 2D 00 2A 00 2D 00 2A 00 2D 00 2A 00 2D 00   // *.-.*.-.*.-.*.-.
                                  2A 00 2D 00 2A 00 2D 00 C4 7B 8B 4F 32 00 5F 00   // *.-.*.-..{.O2._.
                                  61 00 20 00 8B 95 CB 59 02 30 )                   // a. ....Y.0
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  call       string [mscorlib]System.Console::ReadLine()
  IL_0011:  pop
  IL_0012:  newobj     instance void _002_foreach_test.nTest.MyCollection2_a::.ctor()
  IL_0017:  stloc.0
  IL_0018:  ldstr      bytearray (C6 96 08 54 2D 4E 84 76 3C 50 BA 70 1A FF )       // ...T-N.v<P.p..
  IL_001d:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0022:  nop
  IL_0023:  nop
  IL_0024:  ldloc.0
  IL_0025:  callvirt   instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator()
  IL_002a:  stloc.2
  .try
  {
    IL_002b:  br.s       IL_0042
    IL_002d:  ldloc.2
    IL_002e:  callvirt   instance object [mscorlib]System.Collections.IEnumerator::get_Current()
    IL_0033:  unbox.any  [mscorlib]System.Int32
    IL_0038:  stloc.1
    IL_0039:  nop
    IL_003a:  ldloc.1
    IL_003b:  call       void [mscorlib]System.Console::WriteLine(int32)
    IL_0040:  nop
    IL_0041:  nop
    IL_0042:  ldloc.2
    IL_0043:  callvirt   instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
    IL_0048:  stloc.3
    IL_0049:  ldloc.3
    IL_004a:  brtrue.s   IL_002d
    IL_004c:  leave.s    IL_0068
  }  // end .try
  finally
  {
    IL_004e:  ldloc.2
    IL_004f:  isinst     [mscorlib]System.IDisposable
    IL_0054:  stloc.s    CS$0$0002
    IL_0056:  ldloc.s    CS$0$0002
    IL_0058:  ldnull
    IL_0059:  ceq
    IL_005b:  stloc.3
    IL_005c:  ldloc.3
    IL_005d:  brtrue.s   IL_0067
    IL_005f:  ldloc.s    CS$0$0002
    IL_0061:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0066:  nop
    IL_0067:  endfinally
  }  // end handler
  IL_0068:  nop
  IL_0069:  ldstr      bytearray (2A 00 2D 00 2A 00 2D 00 2A 00 2D 00 2A 00 2D 00   // *.-.*.-.*.-.*.-.
                                  2A 00 2D 00 2A 00 2D 00 C4 7B 8B 4F 32 00 5F 00   // *.-.*.-..{.O2._.
                                  61 00 20 00 50 7D 5F 67 02 30 )                   // a. .P}_g.0
  IL_006e:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0073:  nop
  IL_0074:  call       string [mscorlib]System.Console::ReadLine()
  IL_0079:  pop
  IL_007a:  ret
} // end of method Program::Sample_2_a
這裡IL內可以看到命名空間System.Collections
裡面有IEnumerator IEnumerable的介面
並且把它時做出來,這樣便能清楚了解它的運作原理
以上不多做介紹 ,因為大同小異

再來同上題,在加一點點小小的變化
IDisposable 並實作一個 Dispose
用意來釋放資源
Dispose將被GC下一次回收時回收
來看例題
一樣Sample_2_b();呼叫副程式

static void Sample_2_b()
        {          
            Console.WriteLine("*-*-*-*-*-*-範例2_b 開始。"); Console.ReadLine();
            MyCollection2_b col = new MyCollection2_b();
            Console.WriteLine("集合中的值為:");
            // 顯示集合中的項目
            foreach (int i in col)
            {
                Console.WriteLine(i);
            }
            Console.WriteLine("*-*-*-*-*-*-範例2_b 結束。"); Console.ReadLine();
        }


再來看MyCollection2_b.cs程式檔內部的類別與繼承的介面
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Collections;
namespace _002_foreach_test.nTest
{
    // 宣告這個 collection 實做 IEnumerable 介面
    class MyCollection2_b : IEnumerable
    {
        private int[] items;
        public MyCollection2_b()
        {
            this.items = new int[5] { 12, 44, 33, 2, 50 };
        }

        public MyEnumerator2_b MY_GetEnumerator()
        {
            return new MyEnumerator2_b(this);//取得this列舉
            //並且實體化MyEnumerator2_b(col)內部參數
        }

        // 實做 GetEnumerator()
        IEnumerator IEnumerable.GetEnumerator()
        {   //當執行到了foreach中的col會呼叫此方法
            return this.MY_GetEnumerator();//將方法接過去我們自製的方法

            //最後將列舉回傳給IEnumeraor()介面
        }

        // 宣告這個 enumerator 實做 IEnumerator 和 IDisposable 兩個介面
        public class MyEnumerator2_b : IEnumerator, IDisposable
        {
            private int nIndex;

            MyCollection2_b collection;
            public MyEnumerator2_b(MyCollection2_b coll)
            {
                this.collection = coll;//拷貝一份列舉給MyEnumerator2_b
                this.nIndex = -1;//將nIndex初始化
            }

            // 自有變數
            public int Current
            {
                get
                { return (collection.items[nIndex]); }
            }

            // 實做 IEnumerator
            #region IEnumerator 成員

            public void Reset()
            {
                this.nIndex = -1;
            }

            public bool MoveNext()
            {
                this.nIndex++;
                return (this.nIndex < collection.items.GetLength(0));
            }

            object IEnumerator.Current
            {
                get { return (Current); }
            }

            #endregion
            //public void Dispose()
            //{
            //    Console.WriteLine("處理中…");
            //    this.collection = null;
            //}

            // 實做 IDispos
            void IDisposable.Dispose()
            {
                //標註已不用的資源,待GC下一次回收
                Console.WriteLine("處理中…");
                this.collection = null;
            }

        }
       
    }
}


其實這一個範例已經篇題了=V="
IDispos GC 這又是另一門學問了

在來來看最後一個範例
這範例不再實作IEnumerator IEnumerable
直接正常使用foreach 順便看一下IL內部如何規劃

Sample_3() 呼叫副程式 ,內部程式如下

static void Sample_3()
        {
            Console.WriteLine("*-*-*-*-*-*-範例3 開始。"); Console.ReadLine();
            // 宣告一個 Hashtable
            Hashtable ziphash = new Hashtable();
            // 利用 Add() 加入項目 key,values
            ziphash.Add("234", "永和");
            ziphash.Add("613", "朴子");
            ziphash.Add("210", "北竿");
            ziphash.Add("100", "中正區");
            ziphash.Add("805", "旗津區");
            // 顯示內容
            Console.WriteLine("集合中的值為:");
            foreach (string zip in ziphash.Keys)//印出
            {
                Console.WriteLine(zip + "\t" + ziphash[zip]);
            }

            Console.WriteLine("*-*-*-*-*-*-範例3 結束。"); Console.ReadLine();
        }
S

接下來 可以觀察內部IL
將調用GetEnumeraor()方法取得Hashtable中的集合
IL程式碼因為大同小異 所以就不貼了
完!!