背後的故事之 - 快樂的Lambda表達式(二)

黄舟
發布: 2017-02-16 10:52:24
原創
1423 人瀏覽過

上一篇 背後的故事之 – 快樂的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);
}
登入後複製

 以上是背後的故事之 - 快樂的Lambda表達式(二)的內容,更多相關內容請關注PHP中文網(www.php.cn)!


來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!