利用C# 3.0提供的擴展方法技術,可以為已經編譯好的程序集類型增加新的方法,從而應對新的擴展。除了在可擴展性方面所具有的優勢之外,如果能夠合理地結合泛型與類型推斷,擴展方法還可以有效降低代碼的重復,提高程序的可重用性。例如,這樣的方法實現:
public class CustomerDAL { public IEnumerable<Customer> FindCustomers(string roleName) { return from customer in context.Customer where customer.RoleName.Equals(roleName) select customer; } }
當方法返回的結果為null時,采用如下方式進行調用,就會拋出NullReferenceException異常:
Customer customer = new CustomerDAL().FindCustomers(Role.Admin).First();
我們需要對返回結果進行驗證,如果返回為null,則可以拋出自定義異常,或者創建一個空對象,例如:
public IEnumerable<Customer> FindCustomers(string roleName) { IEnumerable<Customer> customers = from customer in context.Customer where customer.RoleName.Equals(roleName) select customer; if (customers == null) { throw new MyException("Cann't find the customers."); } return customers; }
如果系統有許多方法都需要對返回結果進行驗證,則這樣的驗證邏輯就會充斥在各個方法體中,既不利於重用,也會對未來的修改造成極大的阻礙。當然,我們可以引入Null Object模式來替代對null值的判斷邏輯,但這種方式仍然需要為多種類型定義不同的Null Object類型。
Craig Andera在其博客文章中提出使用擴展方法對調用進行驗證。他寫道:
NullReferenceException異常會拋出,但是我們希望有更具體的異常信息。因此,我們編寫了如下的擴展方法:
public static T OrThrow<T>(this T obj, Exception e) { if (obj == null) { throw e; } return obj; }
利用OrThrow擴展方法,則之前的調用方式可以修改為:
Customer customer = new CustomerDAL().FindCustomers(Role.Admin). OrThrow(new MyException("Can't find Customer")).First();
Craig Andera提出:
OrThrow擴展方法對於你所要調用的類型而言是通用的,並且它返回了該類型,所以你可以將其插入到表達式鏈中,而不會丟失智能感應功能。並且因為類型推斷功能,實際上並不需要指定具體的類型。
也就是說,OrThrow擴展方法可以應用到任何類型上,因此它可以在各種類型上重用非空驗證甚至是調用驗證。借鑒這一思想,我們還可以利用此方法默認實現對象實例的創建,以避免拋出NullReferenceException異常,例如:
public static T Instance<T>(this T obj) where T:new() { if (obj == null) { obj = new T(); } return obj; }
由於Instance擴展方法中的類型參數T需要創建實例,因此必須添加new()約束。所以該擴展方法存在一定的局限,例如無法應用在之前的IEnumerable類型上。但對於如下的方法卻非常有效:
public class ListObject { public List<string> Foo() { return null; } }
通過Instance擴展方法,可以安全地調用List的相關屬性和方法,例如Count屬性:
Console.WriteLine(new ListObject().Foo().Instance().Count);控制台打印出來的結果為0。如果沒有Instance擴展方法,則會拋出NullReferenceException異常。
作為C# 3.0增加的新特性,擴展方法在大量項目中得到了廣泛地應用,但絕不僅僅是提高可擴展性這麼簡單。在進行項目開發時,若能適當地考慮使用擴展方法,說不定會帶來出奇制勝的效果。