2014年1月31日 星期五

C# - Exception 例外處理(如同人生,總是會有意外)

人生總是會有例外的時候,而且這個時候,還真不少,記得過年前一個空前勝大的TGS電玩展(紳魔之塔真是破了歷史以來的紀錄),因為工作的關係,而必須要在那辦展。結果回家好死不死的,就被後面的一台機車高速追撞,所幸的是,小弟我安然無恙,要不然可能沒辦法在這邊分享文章(汗)。

人非聖賢,所以寫出的程式,當然也就不能例外了,可能定義了變數,就忘記它真正要使用的時機。再不然就是從DB取出的資料,有時出現的參數為空的忘記判斷,再不然就是寫Socket時,沒有做一些網路存取狀態的處理,等等的,總之就是,想的到的,都是例外。

C# 支援例外處理機制,當有任何的例外錯誤發生時,程式會立刻中斷,然後跳出到外層。此時,如果有任何例外處理的程式 (try … catch) 位於外層,就會接到這個例外,並可以即時處理之。否則,該例外會一直被往外丟,假如都沒有被處理,則程式將被迫中斷,系統會自行輸出例外訊息。

以下是一個會引發例外的程式,由於 a/b = 3/0 會導致嘗試以零除 (System.DivideByZeroException) 的例外,但這個例外又沒有被任何的 try … catch 段落所處理,因此整個程式會中斷並輸出錯誤訊息。

class TryExp
{
    public static void Main(string[] args)
    {
        int a = 3, b = 0;
        Console.WriteLine("a/b=" + a/b);
    }
}

要處理例外可以用 try…catch 語句,以下範例就利用 try { … } catch (DivideByZeroException ex) 捕捉了上述的除以零之例外,開發者可以在 catch 段落中進行例外處理後,再決定要如何繼續執行程式。

class TryExp
{
    public static void Main(string[] args)
    {
        try
        {
             int a = 3, b = 0;
             Console.WriteLine("a/b=" + a / b);
        }
        catch (DivideByZeroException ex)
        {
             Console.WriteLine("被除數不可為 0 !\n"+ex);
        }
    }
}

這樣的結果,就可以較容易在開發時,找出錯誤的部分。程式師要有這樣良好的習慣,對於錯誤的例外處理處理的當,有助於追踨程式。尤其是用在運算,網路程式撰寫方面,有很大的幫力。

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

2014年1月30日 星期四

C# - abstract抽象類別的精神


抽象類別是在物件導向設計上面非常熟不可以當飯吃的基礎,不過其實對於初學者而言,是一個很容易混淆的字眼,重點是在於運作的時機點。抽象類別在Java行之有年了,所以對於精於Java開發的人來說,是一個如切豆腐一樣般的細嫩,要切很簡單,但要切的漂亮,可不是人人都做的很好。

對當開發者定義類別時,可以僅宣告方法名稱而不實作當中的邏輯,這樣的方法稱之為「抽象方法」(Abstract method),如果一個類別中包括了抽象方法,則該類別稱之為「抽象類別」(Abstract class),抽象類別是個未定義完全的類別,所以它不能被用來生成物件,它只能被擴充,並於擴充後完成未完成的抽象方法定義。

Java中要宣告抽象方法與抽象類別,您使用"abstract"關鍵字,直接來看個應用的例子,下面定義一個簡單的比大小遊戲抽象類別,以下是Java的例子:

public abstract class AbstractGuessGame {
    private int number;

    public void setNumber(int number) {
        this.number = number;
    }

    public void start() {
        showMessage("Welcome");

        int guess;
        do {
            guess = getUserInput();
            if(guess > number) {
                showMessage("bigger than the goal number");
            }
            else if(guess < number) {
                showMessage("smaller than the goal number");
            }
            else
                showMessage("you win");
        } while(guess != number);
    }

    protected abstract void showMessage(String message);
    protected abstract int getUserInput();
}

abstract 修飾詞表示要修飾的項目遺失或實作不完整。 abstract 修飾詞可用於類別、方法、屬性、索引子 (Indexer) 和事件。 在類別宣告裡使用 abstract 修飾詞,表示該類別只是當做其他類別的基底類別而已。 成員如果標記為抽象,或是包含在抽象類別 (Abstract Class) 內,則必須由衍生自此抽象類別的類別實作這個成員。

C# 中,繼承和介面實作都是由 : 運算子定義,此運算子就等同於 Java 中的 extends implements。基底類別在類別宣告中應該一直位於最左側。
C# Java 一樣,都不支援多重繼承,也就是說類別無法繼承自一個以上類別。不過,可以和在 Java 中一樣,使用介面來達成這個目的。

抽象類別有下列功能:

  • 抽象類別不能執行個體化。
  • 抽象類別可能會包含抽象方法和存取子。
  • 無法使用 sealed (C# 參考) 修飾詞來修改抽象類別,因為這兩個修飾詞意義剛好相反。 sealed 修飾詞可以不讓類別被繼承,而 abstract 修飾詞卻需要類別被繼承。
  • 衍生自抽象類別的非抽象類別必須包含所有繼承抽象方法和存取子的實作。

在方法或屬性宣告裡使用 abstract 修飾詞,表示該此方法或屬性沒有包含實作。
抽象方法有下列特點:

  • 抽象方法隱含是一個虛擬方法。
  • 抽象方法宣告只允許在抽象類別裡。
  • 因為抽象方法宣告沒有提供實際的實作,因此並沒有方法主體,方法宣告僅以分號做為結束而且簽章 (Signature) 之後沒有大括號 ({ })。 例如:
public abstract void MyMethod();
其實作由非抽象類別的成員覆寫方法 override提供。
  • 在抽象方法宣告中使用 static virtual 修飾詞是錯誤的。
所以精簡來說,抽象類別有這些下列特性 :

  • 抽象類別不可以直接使用new令建立物件實體。
  • 抽象類別必須要被繼承才能使用其功能。
  • 抽象類別中,可以宣告抽象方法。
  • 繼承抽象類別的子類別,必須要實作所有抽象方法。
  • 有些功能,在每一個子類別中都一定會被改寫,父類別就不需要維護該方法的實作定義,這種方法就可以宣告為抽象方法。
  • 抽象方法只能宣告在抽象類別中。
  • 宣告抽象方法時不可以使用private修飾詞。
  • 繼承抽象類別的子類別,必須要實作所有的抽象方法。
  • 若是繼承抽象類別的子類別沒有實作所有的抽象方法,則該子類別就必須要宣告為抽象類別。
  • 抽象類別為一定要被繼承的類別,抽象方法為一定要被改寫的方法
  • 宣告不可以再被繼承的類別,使用 sealed修飾詞。
  • 不想再讓繼承的類別改寫其定義,也可以使用sealed 修飾詞。
在這個範例中,Square 類別必須提供 Area 的實作,因為它衍生自 ShapesClass

abstract class ShapesClass
{
    abstract public int Area();
}

class Square : ShapesClass
{
    int side = 0;

    public Square(int n)
    {
        side = n;
    }

    public override int Area()
    {
        return side * side;
    }

    static void Main()
    {
        Square sq = new Square(12);
        Console.WriteLine("Area of the square = {0}", sq.Area());
    }

    interface I
    {
        void M();
    }

    abstract class C:I
    {
        public abstract void M();

    }
}

假設開發者想要實作一個有視窗介面的比大小遊戲,可以擴充並實作抽象方法,事實上,在很多應用場合都可以見到,對於設計模式上的運用,抽象類別是很常見的,開發者可以善加運用。


-雲遊山水為知已逍遙一生而忘齡- 電腦神手


2014年1月29日 星期三

C# DbContext運用

DbContext是一個執行個體,表示工作單位和儲存機制模式的組合,使其可用來從資料庫查詢並將變更群組在一起,然後這些變更會當做一個單位寫回存放區。 DbContext 在概念上類似於 ObjectContext。

下面是微軟的說明:
DbContext 通常會搭配衍生型別使用,該型別包含模型根實體的 DbSet<TEntity> 屬性。 當建立衍生類別的執行個體時,這些集合會自動初始化。 可以修改這個行為,方法是將 SuppressDbSetInitializationAttribute 屬性 (Attribute) 套用到整個衍生內容類別或是此類別的個別屬性 (Property)。 支援此內容的實體資料模型可以使用幾個方式來指定。 當使用 Code First 方法時,衍生內容上的 DbSet<TEntity> 屬性會用來依照慣例建立模型。 可覆寫受保護的 OnModelCreating 方法來調整此模型。 如果想要對 Model First 方法所使用的模型擁有更多控制權,請從 DbModelBuilder 明確建立 DbCompiledModel,並將這個模型傳遞給其中一個 DbContext 建構函式。 當使用 Database First 或 Model First 方法時,可以使用 Entity Designer 建立實體資料模型 (或是藉由手動建立 EDMX 檔案),然後可以使用實體連接字串或 EntityConnection 物件指定這個模型。 可以使用幾個方法來指定資料庫的連接 (包括資料庫的名稱)。 如果從衍生內容呼叫無參數的 DbContext 建構函式,則會使用衍生內容的名稱在 app.config 或 web.config 檔案中尋找連接字串。 如果找不到連接字串,則會將此名稱傳遞給 Database 類別上註冊的 DefaultConnectionFactory。 然後連接 Factory 會使用此內容名稱當做預設連接字串中的資料庫名稱 (這個預設連接字串會指向 本機電腦上的 .\SQLEXPRESS,除非註冊了不同的 DefaultConnectionFactory)。如果不要使用衍生的內容名稱,也可以明確指定連接/資料庫名稱,方法是將此名稱傳遞給其中一個採用字串的 DbContext 建構函式。 也可以使用 "name=myname" 格式傳遞名稱,這樣必須在組態檔中找到此名稱,否則將會擲回例外狀況。 請注意,app.config 或 web.config 檔案中找到的連接可以是一般資料庫連接字串 (不是特殊的 Entity Framework 連接字串),此時 DbContext 將會使用 Code First。 但是,如果組態檔中找到的連接是特殊的 Entity Framework 連接字串,則 DbContext 將會使用 Database/Model First,而且會使用連接字串中指定的模型。 也可以使用現有或明確建立的 DbConnection 來取代資料庫/連接名稱。 DbModelBuilderVersionAttribute 可以套用至衍生自 DbContext 的類別,以便在建立模型時設定此內容所使用的慣例版本。 如果未套用任何屬性,則會使用最新版本的慣例。

在 ASP.NET MVC 應用程式中使用 Entity Framework 時,DbContext(或 ObjectContext)物件的壽命。

在使用 DbContext 時,大部分用using來確保資源盡快回收,例如:

using (var db = new NorthwindEntites())
{
    // 操作 db 的 entity 集合
}

因此大部分是讓 context 物件與 HTTP request 被C#一起釋放,也就是 one context per request:在 request 開始時建立 DbContext 物件,並且在 request 即將結束時摧毀 context 物件。

一個 Request 配一個 Context

由於 ASP.NET MVC 的  Controller 物件的壽命大約等同一個 request 的壽命,所以一種簡單的作法是在我們的 Controller 類別中建立和摧毀 context 物件。參考底下的程式片段:

public class HomeController : Controller
{
    private NorthwindEntities db = new NorthwindEntities();

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }
}

這個HomeController類別的程式碼,包括建立 context 物件(NorthwindEntites)和 Dispose 方法,全都是 Visual Studio 2012 的 Add Controller 功能幫我產生的,參考下圖:


所有的 Controller 都會用到同一個 DbContext 物件,則可以寫一個 Controller 基礎類別,例如 MyControllerBase,並將 DbContext 變數宣告在 MyControllerBase 類別中,成為其物件成員。

除了讓 context 成為 Controller 物件的成員,另一方法是在 Application 物件的 BeginRequest 事件中建立 context 並存入目前的 HttpContext 的 Items 集合,並且在 EndRequest 事件中摧毀 context 物件,例如:

protected void Application_BeginRequest()
{
    var db = new Models.MoneyUti();
    HttpContext.Current.Items["Northwind"] = db;
}

protected void Application_EndRequest()
{
    var db = HttpContext.Current.Items["MoneyUti"as Models.MoneyUti;
    if (db != null)
    {
        db.Dispose();
        db = null;
    }
}

在 Controller 中取出 context 物件可以如下的方式撰寫:

public ActionResult Index()
{
    var db = this.HttpContext.Items["Northwind"as MoneyUti;
    return View(db.Customers.ToList());
}

上面的程式商業邏輯層中的所有 Business Objects 也可以共用同一個的 DbContext 物件,並享有跨 BOs 交易管理的便利。如果ASP.NET 應用程式會在一個 request 生命週期中建立多條執行緒來分頭(並行)執行多個資料操作,就會有執行上的問題,要另外寫程式碼來處理 synchronization。

對 ASP.NET 應用程式來說,DbContext(或 ObjectContext)的生命週期控制模式通常就是One context per method和 One context per request兩種。如果是 Windows Forms 或 WPF 應用程式,則可以採用 one context per form/window 的方式。

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

2014年1月28日 星期二

C# - abstract、virtual、override、new的差異性

abstract、virtual、override和new是在類別的繼承關係中常用的四個修飾方法的關鍵字,在此略作總結。

1. 常用的中文名稱:
  • abstract => 抽象方法。
  • virtual => 虛擬方法。
  • override => 覆蓋基礎類別方法。
  • new => 隱藏基礎類別方法。
override 和 new 有時都叫覆寫基礎類別方法。

2. 適用場合:
  • abstract 和 virtual 用在基礎類別(父類別)中
  • override 和 new 用在派(衍)生類別(子類別)中。
3. 具體概念:
  • abstract 抽象方法,是空的方法,沒有方法實體,派(衍)生類必須以 override 實現此方法。
  • virtual 虛擬方法,若希望或預料到基礎類別的這個方法在將來的派(衍)生類別中會被覆寫(override 或 new),則此方法必須被聲明為 virtual。
  • override 覆寫繼承自基礎類別的virtural方法,基礎類別方法調用不到。
  • new 隱藏繼承自基礎類別的virtual方法。
  • 當派(衍)生類別中出現與基礎類別同名的方法,而此方法前面未加 override 或 new 修飾符時,編譯器會報警告,但不報錯,真正執行時等同於加了new。


3. abstract 和 virtual 的區別:
  • abstract 方法還沒實現,連累着基礎類別也不能被實例化,除了作為一種規則或符號外沒啥用;virtual 則比較好,派(衍)生類別想覆寫就覆寫,不想覆寫跑原始的。
  • 而且繼承是少用為妙,繼承層次越少越好,派(衍)生類別新擴展的功能越少越好,virtual 深合此意

4. override 和 new 的區別:


  • 當派(衍)生類別對象作為基類類型使用時,override 的執行派(衍)生類別方法,new 的執行基礎類別方法。


  • 作為派(衍)生類別類型調用,則是執行 override 或 new 之後的。
-雲遊山水為知已逍遙一生而忘齡- 電腦神手

2014年1月27日 星期一

C# - Virtual

在我的文章裡有提到Overwrite和New,在這中間的一個運作原理。這中間的一個關鍵字,就是Virturl。它的用途微軟的說明是「修改方法、屬性、索引子 (Indexer) 或事件宣告,以及允許在衍生類別 (Derived Class) 中予以覆寫。 」例如,這個方法可由任一繼承它的類別來覆寫:


當叫用虛擬方法時,會檢查物件的執行階段型別的覆寫成員。 會呼叫大多數衍生類別裡的覆寫成員,而且如果衍生類別都沒有覆寫成員,這可能是原始成員。
根據預設,方法是非虛擬的。 您不能覆寫非虛擬方法。
virtual 修飾詞 (Modifier) 不能與 static、abstract, private 或 override 等修飾詞一起使用。 下列範例說明虛擬屬性:


public class BaseClass
{
    public virtual string GetString()
    {
        return "這是一個方法。";
    }
}


public class DerivedClass : BaseClass
{
    public override string GetString()
    {
        return "這是重寫了方法。";
    }
}

重寫又稱覆蓋、改寫。

  • virtual 用在基類中,指定一個虛方法(屬性),表示這個方法(屬性)可以重寫。
  • override 用在派生類中,表示對基類虛方法(屬性)的重寫。

以上的基底類和派生類都是相對的。B 是 C 的基類,也可以是 A 的派生類,B 中既可以對 A 中的 virtual 虛方法用 override 重寫,也可以指定 virtual 虛方法供 C 重寫。

  • 不能重寫非虛方法或靜態方法。重寫的基底方法必須是 virtual、abstract 或 override 的。為什麼 override 也可以重寫呢?因為基底類別中的 override 實際上是對基類的基類進行的重寫,由於繼承可傳遞,所以也可以對基類中 override 的方法進行重寫。
  • override 聲明不能更改 virtual 方法的可訪問性。override 方法和 virtual 方法必須具有相同的訪問級別修飾符。
  • 不能使用修飾符 new、static、virtual 或 abstract 來修改 override 方法。
  • override屬性聲明必須指定與繼承屬性完全相同的訪問修飾符、類型和名稱,並且被重寫的屬性必須是 virtual、abstract 或 override 的。

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

2014年1月26日 星期日

C# - new和override的使用

對於初學者來說Override和New的使用時機,常常會讓初學者搞不清楚,對於這二個特性極為相似的關鍵字,下面是微軟的描述:

在 C# 中,衍生類別可以包含擁有與基底類別方法相同之名稱的方法。
基底類別方法必須定義為 virtual。
如果衍生類別中的方法前面沒有加上 new 或 override 關鍵字,編譯器會發出警告,且方法的行為會如同有 new 關鍵字一般。
如果衍生類別中的方法前面加上 new 關鍵字,該方法會定義為與基底類別中的方法無關。
如果衍生類別中的方法前面加上 override 關鍵字,則衍生類別的物件會呼叫該方法,而不是基底類別方法。
衍生類別可使用 base 關鍵字呼叫基底類別方法。
override 、virtual 和 new 關鍵字也可用於屬性、索引子和事件。

override
簡單的說,就是Class2繼承Class1,也就是說父類別為Class1,子類別為Class2,換言之Class1為基底類別,Class2為衍生類別,而有時因為繼承的物件( Class2 )的方法( Test )會和父類別( Class1 )的方法 ( Test )有不一樣的處理邏輯,子類別的方法名稱可能會和父類別的方法名稱相同,但其實裡面的邏輯不同,而這個時候,就可以在父類別的方法裡面加上Virtual,子類別裡面的方法加上override,來進行方法邏輯複寫的動作,如下程式碼。

namespace ConsoleApplication1
{
    public class Class1
    {
        public virtual void Test()
        {
            Console.WriteLine("Class1.Test()");
        }
    }

    public class Class2 : Class1
    {
        public override void Test()
        {
            Console.WriteLine("Class2.Test()");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Class2 c = new Class2();
            c.Test();
        }
    }
}


上面的程式碼執行結果會是Class2.Test裡的的法方。

new
對於Overrride的不同,new在運作上有點不同,它必須要仰賴Virtual,。


namespace ConsoleApplication1
{
    public class Class1
    {
        //故意沒加上Virtual
        public void Test()
        {
            Console.WriteLine("Class1.Test()");
        }
    }

    public class Class2 : Class1
    {
        //這裡也不使用override關鍵字來複寫
        public void Test()
        {
            Console.WriteLine("Class2.Test()");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Class2 c = new Class2();
            c.Test();//

        }
    }
}

上面的程式結果,結果還是會出現Class2.Test()
就算不加上new和override還是可以正常編譯,但系統會出現警告提示,雖然說在這方面沒有什麼太大的不同,但是還是最好依循著正確的方式撰寫程式。

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

2014年1月25日 星期六

C# - Abstract & Interface 抽象類別和介面

抽象類別Abstract和介面Interface一直以來是初學者學習物件導向時常常會霧裡探花摸不著頭緒的一項課程,筆者在這邊分享一下自身的經驗談。

抽象類別Abstract:
是用來定義相同種類的物件,所需要具備的共通特性;例如人,是所有人的基底,人有思考、吃、喝、睡覺等等共同的特性,但是每種人行為方式不一樣,所以將這些基本特性,寫成抽象類別與方法,讓其它類別來繼承並且實做方法,這就就抽象類別的意義。

介面Interface:
是用來定義不同種類的物件中,針對某種特性,所需要具備的相同功能;例如走路,機器人,和人類,是兩種不同的類別,但有相同的功能。
另一種思考:
類別是對物件的抽象:建構物件的各式成員與行為模式。
抽象類別是對類別的抽象:提供基底,專門給同性質的類別繼承。
介面是對行為的抽象:任何類別若繼承介面並且實做後,皆具有此功能。

一般來說,interface 較類似抽象類別,但它和Abstract有以下的不同點:
  • interface 裡所有被宣告的 property、method、event 都是 public 的abstract class 裡你可以有 private 和 protected 的相關宣告
  • interface 裡所有被宣告的 property、method 都必需要被繼承且實作才能使用abstract class 你可以不用實作,因為你可以 new 一個 abstract class雖然那沒什麼意義,但是你沒辦法直接 new一個 interface,你必需 new 出繼承了該 interface 並實作的 class,再從那上面取得(Query)該interface。
  • interface 在 C# 裡可以多重繼承,class 只能單一繼承。

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

2014年1月23日 星期四

C# MVC上使用註解

一般的程式上面,都一定會有程式註解這樣的一個功能,方便開發者在每個程式的區塊記錄下程式的意義。對於工作的移交,或者提示在每個變數上的意義。

例如下面的程式註解:

/*...*/ (程式註解) (MDX)

不過對於Visual Studio MVC4來說,寫在cshtml上的註解有點不太一樣,下面的例子:

@* 註解是寫在這裡 *@

如上面所示,它的註解,也是採用@* *@的格式

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

2014年1月22日 星期三

C# Web- 出現 This application is currently offline 問題及解決方法

在用Visual Studio開發web 專案時,有時會遇到一個問題是"This application is currently offline. To enable the application, remove the app_offline.htm file from the application root directory."。


原因是app_offline.htm引起的問題,在網站根目錄下建立app_offline.htm文件來關閉網站是asp.net 2.0中新增的功能,如果網站根目錄下存在這個文件,那麼對任何aspx頁面的顯示的都是app_offline.htm的內容,要使網站正常運行,必須刪除或對app_offline.htm改名。

"This application is currently offline. To enable the application, remove the app_offline.htm file from the application root directory."-解決辦法:找到網站根目錄,刪除其中的app_offline.htm文件即可。

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

2014年1月21日 星期二

C# - 善用程式特性的技巧

C#在程式上面,可以用運它的特性,讓程式寫法更有易讀性,效能更好。其實很程式語言,都有相同的特性,程式寫久了,就會了解最好的程式寫法。

下面這個是一般人字串的寫法:
string name = "";
這寫法易產生誤打空白而不自知,除錯時,反而會造成不容易找出問題而浪被不必要的除錯時間成本。另外,要講效能,當寫出實字 "" 時,會在記憶體空間紀錄上 "",雖然C#中,打在多相同的實字,實際上都會指向同一個地方。建議以下寫法,會減少誤打情況,描述性好:
string name = string.Empty;

用反組譯去看string.Empty會發現它是一個常數,也就是不管寫幾個,在記憶體中只會有一份,並且是運行期才會產生。
網路上有一篇文章在比較差異,實際上 "" 跟 string.Empty 根本沒有效能差異,但是描述性與不小心手滑就有差異了。
經常會要判別是否為空字串,用以下的程式:

string name = string.Empty;
if (name == string.Empty)
{
    //請輸入姓名
}
這樣的寫法有一個小危險,沒有判別是否為null,描述性沒有以下寫法好。
string name = string.Empty;
if (string.IsNullOrEmpty(name))
{
    //請輸入姓名
}
從控制項抓過來的值(大多控制項當沒有值時,會預設空字串),如果確定一定有初始值,
大多時候是要判別有值才執行陳述式
if ( ! string.IsNullOrEmpty(txtName.Text))
{
    //紀錄名稱
}
多一個 not ( ! ),也造成浪費了效能,描述性也差了一點,不易閱讀,因此可以這樣寫:
if (txtName.Text.Length > 0)
{
    //紀錄名稱
}

判斷式的運用對初學者來說,有時候會有無意義的區塊。不易閱讀
if (isTest == action)
{
    //執行測試
}
else
{
    //不執行測試
}

if是一個很玄的東西,它跟switch最大的不同的地方,switch是必然的,絛件只能有一個,但是if可以有很多的絛件。所以很多時候,使用不當時,常常會造成判斷上的誤解。
if (isTest == action)
{
    //執行測試
    return;
}
 //不執行測試

在這時候return會導致不知道何時才是結束時機,建議在最後才寫return。
那就至少要寫成以下的模式,反組譯微軟的code會發現,這是一種慣用手法,
目的在於未來的擴充,不必理會上面判斷式的長城,並且增加閱讀性。
if (isTest == action)
//執行測試
if(isTest != action)
 //不執行測試

 不過這樣的寫法,也要注意,因為它只執行下面程式的第一行,換句話說。下一行不管如何,都會執行到。

if (isTest == action) i = 0;
i = 1;

這樣的程式執行下來,i變數仍然會變成1。這是初學者要注意的使用地方。

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

2014年1月20日 星期一

C# - Generic泛型& Collections集合型別介紹

泛型是程式語言的一種特性。允許開發者在強型別程式語言中編寫代碼時定義一些可變部份,那些部份在使用前必須作出指明。各種程式語言和其編譯器、執行環境對泛型的支援均不一樣。


集合是將密切關聯的資料放在同一個集合中,可以更有效率地來處理這些資料。不需撰寫個別程式碼來處理各個別物件,您可以另行使用相同程式碼以處理集合的所有元素。


一般型別泛型型別
ArrayListList< >
QueueQueue< >
StackStack< >
HashtableDictionary< >
StoredListStoredList< >
ListDictionaryDictionary< >
HybridDictionaryDictionary< >
OrderedDictionaryDictionary< >
SortedDictionaryStoredDictionary< >
NameValueCollectionDictionary< >
DictionaryEntryKeyValPair< >
StringCollectionList<String>
StringDictionaryDictionary<String>

表格左邊一般型別,右邊則是對應的泛型型別,功能就跟一般型別差不多,使是用法不同罷了。假設當你需要撰寫一個功能/類別時,而無得知使用者會使用什麼型態的參數,有可能是string、也有可能是int,用泛型就不用擔心程式被呼叫時還要考慮傳入的型別,只要專心開發功能即可。
標準的集合,在功能上或執行的效率都有限制,System.Collections 命名空間
Hashtable用於包含10個或更多項目的集合,對小集合沒效率
SortedList依索引鍵來排序項目。
集合類別結構
DictionaryEntry主要是用來取出字典集合著Key和Value,可以利用它來逐一處理集合中的項目。
集合類別介面
IDictionary主要是用索引鍵來存取項目,不能用索引值。
IComparer自訂排序與順序的比較
IEqualityComparer只接受相等比較,自訂排序與順序的比較是由 IComparer 介面來提供
比較集合
CaseInsensitiveComparer比較兩個物件是否相等,忽略字串的大小寫。
特殊的集合,彌補標準字典的限制,System.Collections.Specialized 命名空間
ListDictionary用於包含10個或更少項目的集合,對大集合沒效率
HybridDictionary會先實作成ListDictionary的小集合,當內部變大時,將會把自己變成Hashtable的大集合。
OrderedDictionarySortedList類別多了一些功能,更為強大。
字串集合,只能使用字串,如果操作的對象不是物件而是字串,那這個類別是首選,因為它可以省下轉型的動作
StringDictionary索引鍵與資料值均採用強式型別宣告為字串,用法跟Hashtable相似
StringCollection用法跟ArrayList相似
NameValueCollectionStringDictionary相似,差別在於NameValueCollection可以單一索引鍵對應多重值。

CollectionsUtil建立忽略字串大小寫HashtableSortedList的集合。


如何使用泛型型別,以下就以List< >類別來做說明
1.匯入命名空間 System.Collections.Generic
using System.Collections.Generic;
2.引用泛型類別
List<string> myGen = new List<string>();
3.加入集合
myGen.Add("簡單");
myGen.Add("範例");      
4.輸出
foreach (string i in myGen)
{
     Console.WriteLine(i);

}

泛型並不困難,使用上跟一般型別沒什麼不同,
在C#底下就用<傳入的型別>,
例如:List<string> myGen = new List<string>( );

如何自定泛型類別
泛型類別宣告如同一般類別相似,只是宣告內容方法不一樣

泛型類別宣告:
class 類別名稱<T>[where 型別參數: 修件型別]{}

泛型方法宣告:
[存取限制修飾詞]返回型別 方法名稱(T|參數...)[where 型別參數: 絛件型別]{}
where T:struct限制為值型別
where T:class限制為參考型別
where T:new()可以使用 new T()[沒有參數之建構子]
where T:[base class]限制為類別的子類別
where T:[interface]限制必須實作介面
where T:U(U是型別參數)限制為U的子類別

1.宣告泛型類別
class 類別名稱<T1,T2,....Tn)>
{ 

}

其中以角括弧 < > 符號包住的 T1, T2,..., Tn 即是所謂的型別參數(type parameters),也就是先前提過的「變動的部分」。型別參數可有一至多個,端看實際的需要而定。慣例上,型別參數的名稱通常以一個大寫英文字母 ‘T’ 開頭,如果只有一個型別參數,通常就單純命名為 “T”,例如:List<T>。若有多個型別參數,則建議使用望文生義的名稱,以便識別。像 .NET 的泛型集合 Dictionary<TKey, TValue> 就很清楚,一看就知道這個泛型集合所要儲存的每一筆資料都包含兩種元素:key(索引鍵)與value(資料值),而這兩種元素的真正型別,則是等到實際要使用集合物件的時候才以參數的方式帶入。

2.宣告泛型方法

public class myGeneric<T>
{
    public void myTest(T myValue)
    {
        System.Type myType = myValue.GetType();
        Console.WriteLine(myType);
    }
}

3.引用泛型類別

myGeneric<int> myCom1 =new myGeneric<int>();
myCom1.myTest(1);
myGeneric<string> myCom2 = new myGeneric<string>();
myCom2.myTest("sdf");
myGeneric<double> myCom3 = new myGeneric<double>();

myCom3.myTest(12.2344);

不管引用任何型態,泛型類別都能全部接收。
運用泛型和集合,可以將資料類型作型別的分類,將特定型態的資料,透過集合來存取。
以前的傳統程式,要寫出這樣的功能,需要個別去寫出一個類別去使用,在.Net裡可以很輕易的就運用這樣的功能。

-雲遊山水為知已逍遙一生而忘齡- 電腦神手