快樂的Lambda表達式 上一篇 背後的故事之 - 快樂的Lambda表達式(一)我們由淺入深的分析了一下Lambda表達式。知道了它和委托以及普通方法的區別,並且通過測試對比他們之間的性能,然後我們通過IL代碼深入瞭解了Lambda表達式,以及介紹瞭如何在.NET中用Lambda表達式來實現Jav ...
快樂的Lambda表達式
上一篇 背後的故事之 - 快樂的Lambda表達式(一)我們由淺入深的分析了一下Lambda表達式。知道了它和委托以及普通方法的區別,並且通過測試對比他們之間的性能,然後我們通過IL代碼深入瞭解了Lambda表達式,以及介紹瞭如何在.NET中用Lambda表達式來實現JavaScript中流行的一些模式。
今天,我們接著來看Lambda表達式在.NET中還有哪些新鮮的玩法。
Lambda表達式玩轉多態
Lambda如何實現多態?我們用抽象類和虛方法了,為什麼還要用Lambda這個玩意?且看下麵的代碼:
class MyBaseClass { public Action SomeAction { get; protected set; } public MyBaseClass() { SomeAction = () => { //Do something! }; } } class MyInheritedClass : MyBaseClass { public MyInheritedClass() { SomeAction = () => { //Do something different! }; } }
我們的基類不是抽象類,也沒有虛方法,但是把屬性通過委托的方式暴露出來,然後在子類中重新為我們的SomeAction賦予一個新的表達式。這就是我們實現多態的過程,當然父類中的SomeAction的set有protected的保護級別,不然就會被外部隨易修改了。但是這還不完美,父類的SomeAction在子類中被覆蓋之後,我們徹底訪問不到它了,要知道真實情況是我們可以通過base來訪問父類原來的方法的。接下來就是實現這個了:
class MyBaseClass { public Action SomeAction { get; private set; } Stack<Action> previousActions; protected void AddSomeAction(Action newMethod) { previousActions.Push(SomeAction); SomeAction = newMethod; } protected void RemoveSomeAction() { if(previousActions.Count == 0) return; SomeAction = previousActions.Pop(); } public MyBaseClass() { previousActions = new Stack<Action>(); SomeAction = () => { //Do something! }; } }
上面的代碼中,我們通過AddSomeAction來實現覆蓋的同時,將原來的方法保存在previousActions中。這樣我們就可以保持兩者同時存在了。
大家知道子類是不能覆蓋父類的靜態方法的,但是假設我們想實現靜態方法的覆蓋呢?
void Main() { var mother = HotDaughter.Activator().Message; //mother = "I am the mother" var create = new HotDaughter(); var daughter = HotDaughter.Activator().Message; //daughter = "I am the daughter" } class CoolMother { public static Func<CoolMother> Activator { get; protected set; } //We are only doing this to avoid NULL references! static CoolMother() { Activator = () => new CoolMother(); } public CoolMother() { //Message of every mother Message = "I am the mother"; } public string Message { get; protected set; } } class HotDaughter : CoolMother { public HotDaughter() { //Once this constructor has been "touched" we set the Activator ... Activator = () => new HotDaughter(); //Message of every daughter Message = "I am the daughter"; } }
這裡還是利用了將Lambda表達式作為屬性,可以隨時重新賦值的特點。當然這隻是一個簡單的示例,真實項目中並不建議大家這麼去做。
方法字典
實際上這個模式我們在上一篇的返回方法中已經講到了,只是沒有這樣一個名字而已,就算是一個總結吧。故事是這樣的,你是不是經常會寫到switch-case語句的時候覺得不夠優雅?但是你又不想去整個什麼工廠模式或者策略模式,那怎麼樣讓你的代碼看起來高級一點呢?
public Action GetFinalizer(string input) { switch { case "random": return () => { /* ... */ }; case "dynamic": return () => { /* ... */ }; default: return () => { /* ... */ }; } } //-------------------變身之後----------------------- Dictionary<string, Action> finalizers; public void BuildFinalizers() { finalizers = new Dictionary<string, Action>(); finalizers.Add("random", () => { /* ... */ }); finalizers.Add("dynamic", () => { /* ... */ }); } public Action GetFinalizer(string input) { if(finalizers.ContainsKey(input)) return finalizers[input]; return () => { /* ... */ }; }
好像看起來是不一樣了,有那麼一點味道。但是一想是所有的方法都要放到那個BuildFinalizers裡面,這種組織方法實在是難以接受,我們來學學插件開發的方式,讓它自己去找所有我們需要的方法。
static Dictionary<string, Action> finalizers; // 在靜態的構造函數用調用這個方法 public static void BuildFinalizers() { finalizers = new Dictionary<string, Action>(); // 獲得當前運行程式集下所有的類型 var types = Assembly.GetExecutingAssembly().GetTypes(); foreach(var type in types) { // 檢查類型,我們可以提前定義介面或抽象類 if(type.IsSubclassOf(typeof(MyMotherClass))) { // 獲得預設無參構造函數 var m = type.GetConstructor(Type.EmptyTypes); // 調用這個預設的無參構造函數 if(m != null) { var instance = m.Invoke(null) as MyMotherClass; var name = type.Name.Remove("Mother"); var method = instance.MyMethod; finalizers.Add(name, method); } } } } public Action GetFinalizer(string input) { if(finalizers.ContainsKey(input)) return finalizers[input]; return () => { /* ... */ }; }
如果要實現插件化的話,我們不光要能夠載入本程式集下的方法,還要能隨時甚至運行時去載入外部的方法,請繼續往下看:
internal static void BuildInitialFinalizers() { finalizers = new Dictionary<string, Action>(); LoadPlugin(Assembly.GetExecutingAssembly()); } public static void LoadPlugin(Assembly assembly) { var types = assembly.GetTypes(); foreach(var type in types) { if(type.IsSubclassOf(typeof(MyMotherClass))) { var m = type.GetConstructor(Type.EmptyTypes); if(m != null) { var instance = m.Invoke(null) as MyMotherClass; var name = type.Name.Remove("Mother"); var method = instance.MyMethod; finalizers.Add(name, method); } } } }
現在,我們就可以用這個方法,給它指定程式集去載入我們需要的東西了。
最後留給大家一個問題,我們能寫遞歸表達式麽?下麵的方法如果用表達式如何寫呢?
int factorial(int n) { if(n == 0) return 1; else return n * factorial(n - 1); }