How Lua implements object-oriented

As we all know, Lua, as a small but flexible scripting language, is now widely used in hot updates of various online games. After all, it is an interpreted language and does not need to be compiled in advance. We can make it into an AssetBundle and decompress and run it on site.

However, I believe that many children's shoes for game development may have sighed that Lua is really difficult to use when they switch from strongly typed languages ​​​​such as C# to write Lua code! ! ! It takes a long time to find a reference, and I often type the wrong field. More importantly, it does not support object-oriented! I'm already single, do you still want to deprive me of the right to new objects? ╥﹏╥

However, with an in-depth understanding of Lua, I find it very fascinating, and the predecessors of the above problems have long had solutions.

This article will talk about how Lua implements object-oriented.

Taking C# as an example, this is how I understand class. Class is a data structure that combines state (field) and operation (method or function) in one unit. We can use this class to dynamically create instances . These instances are also called objects.

For example, we define a class bird,

public class Bird
{
    public int color;
    public bool shitOnPeople;

    public void Fly()
    {
    }
}

Through this bird template, several new birds were created. The fields of these birds may be different. There are colored birds, black birds, and some birds like to shit on people's heads... But they all have the ability of birds, such as flying .

So, is there anything similar to class in Lua? Let's see, the number is wrong, the string is wrong, table... this seems to work. So let's write it down and try:

Bird = {}

Bird.color = Color.black
Bird.shitOnPeople = true

Bird.Fly = function() end

It doesn't seem right, it's an example to write it like this. This is a white bird that likes to shit on people's heads, not birds in general.

So how to implement classes with lua? Let's first consider the characteristics of C# classes:

Classes support inheritance and polymorphism.

Let's ignore polymorphism for now, let's look at inheritance first. Now define a magpie, inherited from Bird:

public class Swallow : Bird
{
    public void Sing()
    {
    }
}

Although we didn't write the color field and the Fly method, we can still access these things with the magpie through the inherited features. In other words, if a subclass accesses a method (or field) that is not defined by itself, it will go to the method (or field) in the parent class.

Wait, it feels so familiar, isn't this the metatable in Lua? !

When lua looks up the elements in the table, the rules are as follows:

 1. Search in the table, if found, return the element, if not found, continue
 2. Determine whether the table has a metatable, if there is no metatable, return nil, if there is a metatable, continue
 3. Determine whether the metatable has_ _index method, if the __index method is nil, return nil; if the __index method is a table, repeat 1, 2, 3; if the __index method is a function, return the return value of the function

Perfect, let's try it.

Bird = {}

Bird.color = white
Bird.shitOnPeople = false

Bird.Fly = function()
    print('i can fly~')
end


Swallow = {}
Swallow.color = black
Swallow.Sing = function()
    print('i can sing~~')
end

setmetatable(Swallow, {__index = Bird})


Swallow.Fly()

The result of the operation is as follows:

Let's encapsulate it, don't write the sentence setmetatable every time, and make it more like C#.

Class = function(class)
    local proto = {}
    setmetatable(proto, {__index = class})

    proto.New = function()
        return setmetatable({}, {__index = proto})
    end

    return proto
end


Swallow = Class(Bird)
Swallow.color = black
Swallow.Sing = function()
    print('i can sing~~')
end

-- setmetatable(Swallow, {__index = Bird})

Swallow1 = Swallow.New()
Swallow2 = Swallow.New()

Swallow1.Fly()
Swallow2.Sing()

Run it:

Perfect!

To explain the above code a little bit, we used two setmetatables, the first one is used on proto, proto is equivalent to Class in c#. We added a new method to it, and in the new method, we returned an empty table with proto as the metatable, which is equivalent to an instance in c#.

However, this is not object-oriented enough, we still lack a constructor, let's add it

Class = function(base)
    local proto = {}
    setmetatable(proto, {__index = base})
    proto.base = base
    proto.New = function(...)
        local obj = setmetatable({}, {__index = proto})
        -- 递归的执行父类构造函数
        local create
        create = function(c, ...)
            if c.base then
                create(c.base,...)
            end
            if c.Ctor then
                c.Ctor(obj,...)
            end
        end

        create(proto, ...)
        return obj
    end

    return proto
end

By the way, add a __classtype field to proto to realize the is function in C#:

proto.__classtype = proto

At this point, the goal is basically achieved.

Attach the source code address:

https://github.com/787786815/LuaOOP.giticon-default.png?t=M276https://github.com/787786815/LuaOOP.git

Guess you like

Origin blog.csdn.net/qq_17758883/article/details/123241832