背后的故事之 - 快樂的Lambda表達(dá)式(二)

字號:


    
    上一篇 背后的故事之 – 快樂的Lambda表達(dá)式(一)我們由淺入深的分析了一下Lambda表達(dá)式。知道了它和委托以及普通方法的區(qū)別,并且通過測試對比他們之間的性能,然后我們通過IL代碼深入了解了Lambda表達(dá)式,以及介紹了如何在.NET中用Lambda表達(dá)式來實現(xiàn)JavaScript中流行的一些模式。
    今天,我們接著來看Lambda表達(dá)式在.NET中還有哪些新鮮的玩法。
    Lambda表達(dá)式玩轉(zhuǎn)多態(tài)
    Lambda如何實現(xiàn)多態(tài)?我們用抽象類和虛方法了,為什么還要用Lambda這個玩意?且看下面的代碼:
    class MyBaseClass
    {
    public Action SomeAction { get; protected set; }
    public MyBaseClass()
    {
    SomeAction = () =>
    {
    //Do something!
    };
    }
    }
    class MyInheritedClass : MyBaseClass
    {
    public MyInheritedClass()
    {
    SomeAction = () => {
    //Do something different!
    };
    }
    }
    我們的基類不是抽象類,也沒有虛方法,但是把屬性通過委托的方式暴露出來,然后在子類中重新為我們的SomeAction賦予一個新的表達(dá)式。這就是我們實現(xiàn)多態(tài)的過程,當(dāng)然父類中的SomeAction的set有protected的保護(hù)級別,不然就會被外部隨易修改了。但是這還不完美,父類的SomeAction在子類中被覆蓋之后,我們徹底訪問不到它了,要知道真實情況是我們可以通過base來訪問父類原來的方法的。接下來就是實現(xiàn)這個了
    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來實現(xiàn)覆蓋的同時,將原來的方法保存在previousActions中。這樣我們就可以保持兩者同時存在了。
    大家知道子類是不能覆蓋父類的靜態(tài)方法的,但是假設(shè)我們想實現(xiàn)靜態(tài)方法的覆蓋呢?
    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表達(dá)式作為屬性,可以隨時重新賦值的特點。當(dāng)然這只是一個簡單的示例,真實項目中并不建議大家這么去做。
    方法字典
    實際上這個模式我們在上一篇的返回方法中已經(jīng)講到了,只是沒有這樣一個名字而已,就算是一個總結(jié)吧。故事是這樣的,你是不是經(jīng)常會寫到switch-case語句的時候覺得不夠優(yōu)雅?但是你又不想去整個什么工廠模式或者策略模式,那怎么樣讓你的代碼看起來高級一點呢?
    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里面,這種組織方法實在是難以接受,我們來學(xué)學(xué)插件開發(fā)的方式,讓它自己去找所有我們需要的方法。
    static Dictionary<string, Action> finalizers;
    // 在靜態(tài)的構(gòu)造函數(shù)用調(diào)用這個方法
    public static void BuildFinalizers()
    {
    finalizers = new Dictionary<string, Action>();
    // 獲得當(dāng)前運行程序集下所有的類型
    var types = Assembly.GetExecutingAssembly().GetTypes();
    foreach(var type in types)
    {
    // 檢查類型,我們可以提前定義接口或抽象類
    if(type.IsSubclassOf(typeof(MyMotherClass)))
    {
    // 獲得默認(rèn)無參構(gòu)造函數(shù)
    var m = type.GetConstructor(Type.EmptyTypes);
    // 調(diào)用這個默認(rèn)的無參構(gòu)造函數(shù)
    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 () => { /* ... */ };
    }
    如果要實現(xiàn)插件化的話,我們不光要能夠加載本程序集下的方法,還要能隨時甚至運行時去加載外部的方法,請繼續(xù)往下看:
    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);
    }
    }
    }
    }
    現(xiàn)在,我們就可以用這個方法,給它指定程序集去加載我們需要的東西了。
    最后留給大家一個問題,我們能寫遞歸表達(dá)式么?下面的方法如果用表達(dá)式如何寫呢?
    int factorial(int n)
    {
    if(n == 0)
    return 1;
    else
    return n * factorial(n - 1);
    }