Menu
CoddyTech

الـ Metamethod المسماة __newindex

جزء من قسم المنطق وتسلسل التنفيذ في رحلة Lua على Coddy. الدرس 19 من 54.

لقد تعلّمت كيف يتحكّم __index فيما يحدث عندما تحاول قراءة مفتاح غير موجود في جدول. والآن ستكتشف نظيره: __newindex، الذي يتحكّم فيما يحدث عندما تُسنِد قيمة إلى مفتاح لم يتضمّنه الجدول بعد.

تُستدعى الطريقة الوصفية __newindex كلما حاولت إسناد قيمة إلى مفتاح غير موجود حاليًا في الجدول. وهذا يمنحك القدرة على اعتراض التعديلات والتحكم فيها قبل حدوثها.

إليك مثالًا بسيطًا يمنع إضافة أي مفاتيح جديدة إلى جدول:

local readOnly = {}
local meta = {
    __newindex = function(table, key, value)
        print("Cannot add key: " .. key)
    end
}

setmetatable(readOnly, meta)

readOnly.name = "Alice"  -- الناتج: Cannot add key: name
print(readOnly.name)     -- الناتج: nil

عندما تحاول تعيين readOnly.name = "Alice"، ترى Lua أن name غير موجود في الجدول. وبدلًا من إضافته، تستدعي Lua الدالة __newindex بثلاثة وسائط: الجدول نفسه، والمفتاح الذي يجري تعيينه، والقيمة التي يجري إسنادها.

في هذا المثال، تطبع الدالة رسالة فحسب ولا تخزّن أي شيء فعليًا.

يمكنك أيضًا استخدام __newindex للتحقق من صحة البيانات قبل السماح بإضافتها إلى جدولك:

local player = {}
local meta = {
    __newindex = function(t, key, value)
        if key == "health" and type(value) ~= "number" then
            print("Health must be a number!")
        else
            rawset(t, key, value)
        end
    end
}

setmetatable(player, meta)

player.health = "high"  -- المخرجات: Health must be a number!
player.health = 100     -- مسموح به، ويُخزَّن باستخدام rawset
player.health = "high"  -- لا تظهر رسالة: health موجودة الآن

لاحظ استخدام rawset() في المثال الثاني. تتجاوز هذه الدالة الجدول الفوقي وتضبط القيمة مباشرةً في الجدول.

من دونه، سيؤدي تعيين القيمة إلى تشغيل __newindex مرة أخرى، ما ينشئ حلقة لا نهائية.

بما أن __newindex لا يعمل إلا مع المفاتيح غير الموجودة في الجدول، فإن هذا الفحص يحمي الإسناد الأول لكل مفتاح. بعد أن يخزّن rawset القيمة health، تغيّر الإسنادات اللاحقة قيمته مباشرةً. للتحقق من كل إسناد، اترك الجدول نفسه فارغًا وخزّن القيم في جدول ثانٍ يقرأ منه عبر __index:

local data = {}
local player = setmetatable({}, {
    __index = data,
    __newindex = function(t, key, value)
        if key == "health" and type(value) ~= "number" then
            print("Health must be a number!")
        else
            data[key] = value
        end
    end
})

player.health = 100
player.health = "high"  -- المخرجات: Health must be a number!
print(player.health)    -- المخرجات: 100

لا يحصل player على مفتاح خاص به أبدًا، لذا تمر كل عملية إسناد عبر __newindex، وكل عملية قراءة تصل إلى data عبر __index.

challenge icon

التحدي

سهل

اكتب دالة createValidatedTable تأخذ key وvalue وتُعيد جدولًا لا يقبل إلا القيم النصية للمفاتيح الجديدة.

أنشئ جدولًا فارغًا وأرفق به جدولًا وصفيًا يحتوي على __newindex للتحقق من نوع القيمة. إذا حاول أحدهم إضافة قيمة غير نصية، فاستخدم rawset() لتخزين النص "invalid" بدلًا منها. أما القيم النصية، فخزّنها بشكل عادي باستخدام rawset().

المنطق:

  • أنشئ جدولًا فارغًا
  • أنشئ جدولًا وصفيًا يحتوي على __newindex يستقبل الجدول والمفتاح والقيمة
  • داخل __newindex، تحقّق مما إذا كانت القيمة نصية باستخدام type(value) == "string"
  • إذا كانت نصية، فاستخدم rawset(t, key, value) لتخزينها
  • إذا لم تكن نصية، فاستخدم rawset(t, key, "invalid") لتخزين "invalid" بدلًا منها
  • أرفق الجدول الوصفي بالجدول باستخدام setmetatable()
  • استخدم rawset() لإضافة زوج المفتاح والقيمة الأولي إلى الجدول (متجاوزًا الجدول الوصفي)
  • أعِد الجدول

المعاملات:

  • key (نص): المفتاح الأولي المراد إضافته إلى الجدول
  • value (نص): القيمة الأولية المراد إضافتها إلى الجدول

القيمة المُعادة: جدول يتحقق من القيم ولا يقبل إلا القيم النصية، ويحتوي على زوج المفتاح والقيمة الأولي (جدول)

جرّب بنفسك

function createValidatedTable(key, value)
    -- اكتب الكود هنا
end
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس المنطق وتسلسل التنفيذ

تدرّب بنفسك: مترجم Lua عبر الإنترنت