Home  >  Article  >  Backend Development  >  The story behind it - Happy Lambda Expression (2)

The story behind it - Happy Lambda Expression (2)

黄舟
黄舟Original
2017-02-16 10:52:241302browse

Previous article The story behind it – Happy Lambda Expression (1) We analyzed the Lambda expression from the shallower to the deeper. We know the difference between it and delegates and ordinary methods, and compare their performance through testing. Then we have an in-depth understanding of Lambda expressions through IL code, and introduce how to use Lambda expressions in .NET to implement popular JavaScript some patterns.

Today, let’s take a look at what other new ways Lambda expressions can be used in .NET.

Lambda expressions play with polymorphism

How does Lambda implement polymorphism? We use abstract classes and virtual methods, why do we still use Lambda? Let’s look at the following code:

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

Our base class is not an abstract class and has no virtual methods, but it exposes the properties through delegation, and then re-assigns a new one to our SomeAction in the subclass. expression. This is our process of realizing polymorphism. Of course, the set of SomeAction in the parent class has a protected protection level, otherwise it will be easily modified by the outside. But this is not perfect yet. After the SomeAction of the parent class is overwritten in the subclass, we cannot access it at all. The real situation is that we can access the original method of the parent class through base. The next step is to implement this

class MyBaseClass
{
    public Action SomeAction { get; private set; }
 
    Stack 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();
 
        SomeAction = () => {
            //Do something!
        };
    }
}

In the above code, we use AddSomeAction to achieve overwriting and at the same time save the original method in previousActions. This way we can keep both present at the same time.

Everyone knows that subclasses cannot override static methods of parent classes, but what if we want to implement static method coverage?

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

Here we still take advantage of the feature of using Lambda expressions as attributes that can be reassigned at any time. Of course, this is just a simple example, and it is not recommended for everyone to do this in real projects.

Method Dictionary

In fact, we have already mentioned this mode in the return method of the previous article, but there is no such name, so it is just a summary. The story is like this, do you often feel that it is not elegant enough when writing switch-case statements? But you don’t want to go into the whole factory mode or strategy mode, so how to make your code look more advanced?

public Action GetFinalizer(string input)
{
    switch
    {
        case "random":
            return () => { /* ... */ };
        case "dynamic":
            return () => { /* ... */ };
        default:
            return () => { /* ... */ };
    }
}
 
//-------------------变身之后-----------------------
Dictionary finalizers;
 
public void BuildFinalizers()
{
    finalizers = new Dictionary();
    finalizers.Add("random", () => { /* ... */ });
    finalizers.Add("dynamic", () => { /* ... */ });
}
 
public Action GetFinalizer(string input)
{
    if(finalizers.ContainsKey(input))
        return finalizers[input];
 
    return () => { /* ... */ };
}

It seems to look different, with a little taste. But when I think about it, all methods must be put into the BuildFinalizers. This organizational method is really unacceptable. Let's learn the plug-in development method and let it find all the methods we need by itself.

static Dictionary finalizers;
 
// 在静态的构造函数用调用这个方法
public static void BuildFinalizers()
{
    finalizers = new Dictionary();
 
    // 获得当前运行程序集下所有的类型
    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 () => { /* ... */ };
}

If we want to implement plug-in, we must not only be able to load the methods in this assembly, but also be able to load external methods at any time or even during runtime. Please continue reading:

internal static void BuildInitialFinalizers()
{
    finalizers = new Dictionary();
    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);
            }
        }
    }
}

Now, we can use this method to specify the assembly to load what we need.

Finally, I leave you with a question, can we write recursive expressions? How to write the following method using expressions?

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

The above is the content of the story behind - Happy Lambda Expression (2). For more related content, please pay attention to the PHP Chinese website (m.sbmmt.com)!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn