Heim  >  Artikel  >  Backend-Entwicklung  >  Die Geschichte dahinter – Happy Lambda Expression (2)

Die Geschichte dahinter – Happy Lambda Expression (2)

黄舟
黄舟Original
2017-02-16 10:52:241422Durchsuche

Vorheriger Artikel Die Geschichte dahinter – Happy Lambda Expression (1) Wir haben den Lambda-Ausdruck von flacher nach tiefer analysiert. Wir kennen den Unterschied zu Delegaten und gewöhnlichen Methoden und vergleichen ihre Leistung durch Tests. Anschließend erhalten wir ein tiefgreifendes Verständnis von Lambda-Ausdrücken durch IL-Code und stellen vor, wie Lambda-Ausdrücke in .NET verwendet werden, um einige beliebte JavaScript-Muster zu implementieren .

Werfen wir heute einen Blick auf andere neue Möglichkeiten zur Verwendung von Lambda-Ausdrücken in .NET.

Lambda-Ausdrücke spielen mit Polymorphismus

Wie implementiert Lambda Polymorphismus? Wir verwenden abstrakte Klassen und virtuelle Methoden. Warum verwenden wir immer noch Lambda? Schauen wir uns den folgenden Code an:

class MyBaseClass
{
    public Action SomeAction { get; protected set; }
 
    public MyBaseClass()
    {
        SomeAction = () =>
        {
            //Do something!
        };
    }
}
 
class MyInheritedClass : MyBaseClass
{
    public MyInheritedClass()
    {
        SomeAction = () => {
            //Do something different!
        };
    }
}

Unsere Basisklasse ist keine abstrakte Klasse und verfügt über keine virtuellen Methoden, aber sie macht die Eigenschaften durch Delegation verfügbar und weist sie dann unserer SomeAction in der Unterklasse neu zu . neuer Ausdruck. Dies ist unser Prozess zur Realisierung von Polymorphismus. Natürlich verfügt die Menge von SomeAction in der übergeordneten Klasse über eine geschützte Schutzebene, da sie sonst leicht von außen geändert werden kann. Dies ist jedoch noch nicht perfekt, nachdem die SomeAction der übergeordneten Klasse in der Unterklasse überschrieben wurde. Die tatsächliche Situation ist, dass wir über base auf die ursprüngliche Methode der übergeordneten Klasse zugreifen können. Der nächste Schritt besteht darin, dies zu implementieren

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!
        };
    }
}

Im obigen Code verwenden wir AddSomeAction, um die ursprüngliche Methode in previousActions zu überschreiben und zu speichern. Auf diese Weise können wir beide gleichzeitig existieren lassen.

Jeder weiß, dass Unterklassen die statischen Methoden der übergeordneten Klassen nicht überschreiben können, aber was ist, wenn wir die Abdeckung statischer Methoden implementieren möchten?

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";
    }
}

Hier machen wir uns noch zunutze, dass Lambda-Ausdrücke als Attribute verwendet werden und jederzeit neu zugewiesen werden können. Dies ist natürlich nur ein einfaches Beispiel und es wird nicht jedem empfohlen, dies in realen Projekten zu tun.

Methodenwörterbuch

Tatsächlich haben wir diesen Modus bereits in der Rückgabemethode des vorherigen Artikels erwähnt, aber es gibt keinen solchen Namen, es handelt sich also nur um eine Zusammenfassung. Die Geschichte sieht so aus: Haben Sie oft das Gefühl, dass Anweisungen zum Wechseln der Groß- und Kleinschreibung nicht elegant genug sind, wenn Sie sie schreiben? Sie möchten jedoch nicht in den gesamten Fabrik- oder Strategiemodus wechseln. Wie können Sie Ihren Code also fortschrittlicher aussehen lassen?

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 () => { /* ... */ };
}

Es scheint anders auszusehen, mit ein wenig Vorgeschmack. Aber wenn ich darüber nachdenke, müssen alle Methoden in die BuildFinalizers eingefügt werden. Diese Organisationsmethode ist wirklich inakzeptabel. Lassen Sie uns die Plug-in-Entwicklungsmethode erlernen und alle Methoden, die wir benötigen, selbst finden.

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 () => { /* ... */ };
}

Wenn wir Plug-Ins implementieren möchten, müssen wir nicht nur die Methoden in dieser Assembly laden können, sondern auch jederzeit oder sogar während der Laufzeit externe Methoden laden können. Bitte fahren Sie fort lesen:

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);
            }
        }
    }
}

Jetzt können wir diese Methode verwenden, um die Assembly anzugeben, die geladen werden soll, was wir brauchen.

Abschließend möchte ich Ihnen noch eine Frage stellen: Können wir rekursive Ausdrücke schreiben? Wie schreibe ich die folgende Methode mithilfe von Ausdrücken?

int factorial(int n)
{
    if(n == 0)
        return 1;
    else
        return n * factorial(n - 1);
}

Das Obige ist der Inhalt der Geschichte dahinter – Happy Lambda Expression (2). Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn)!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn