Menu
Coddy logo textTech

__newindex メタメソッド

CoddyのLuaジャーニー「Logic & Flow」セクションの一部 — レッスン 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は3つの引数(テーブル自体、設定されるキー、および割り当てられる値)を指定して __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     -- 許可されます

2番目の例での rawset() の使用に注目してください。この関数はメタテーブルをバイパスし、テーブルに直接値を設定します。

それがないと、値を設定することで再び __newindex がトリガーされ、無限ループが発生してしまいます。

challenge icon

チャレンジ

簡単

keyvalue を受け取り、新しいキーに対して文字列の値のみを受け入れるテーブルを返す関数 createValidatedTable を作成してください。

空のテーブルを作成し、値の型を検証する __newindex を持つメタテーブルをアタッチします。誰かが文字列以外の値を追加しようとした場合は、rawset() を使用して代わりに文字列 "invalid" を格納します。文字列値の場合は、rawset() を使用して通常通り格納します。

ロジック:

  • 空のテーブルを作成します
  • テーブル、キー、および値を受け取る __newindex を持つメタテーブルを作成します
  • __newindex の内部で、type(value) == "string" を使用して値が文字列かどうかを確認します
  • 文字列である場合は、rawset(t, key, value) を使用して格納します
  • 文字列でない場合は、rawset(t, key, "invalid") を使用して代わりに "invalid" を格納します
  • setmetatable() を使用してメタテーブルをテーブルにアタッチします
  • rawset() を使用して、最初のキーと値のペアをテーブルに追加します(メタテーブルをバイパスします)
  • テーブルを返します

パラメータ:

  • key (string): テーブルに追加する最初のキー
  • value (string): テーブルに追加する最初の値

戻り値: 文字列値のみを受け入れる検証機能付きのテーブル。最初のキーと値のペアが含まれています (table)

チートシート

__newindex メタメソッドは、テーブルに存在しないキーに値を代入しようとしたときにトリガーされ、変更をインターセプトして制御することができます。

新しいキーの追加を防ぐ基本的な例:

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

setmetatable(readOnly, meta)

readOnly.name = "Alice"  -- Output: Cannot add key: name
print(readOnly.name)     -- Output: nil

__newindex 関数は、テーブル自体、設定されるキー、および代入される値の3つの引数を受け取ります。

__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"  -- Output: Health must be a number!
player.health = 100     -- Allowed

メタテーブルをバイパスしてテーブルに直接値を設定するには、rawset(t, key, value) を使用します。これにより、__newindex 内で値を設定する際の無限ループを防ぐことができます。

自分で試してみよう

function createValidatedTable(key, value)
    -- ここにコードを記述してください
end
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

Logic & Flowのすべてのレッスン