Metametoda __newindex
Część sekcji Logika i przepływ programu ścieżki Lua w Coddy. Lekcja 19 z 54.
Dowiedziałeś się, jak __index kontroluje to, co się dzieje, gdy próbujesz odczytać klucz, który nie istnieje w tablicy. Teraz poznasz jego odpowiednik: __newindex, który kontroluje to, co się dzieje, gdy przypisujesz wartość do klucza, którego tablica jeszcze nie zawiera.
Metametoda __newindex jest wywoływana za każdym razem, gdy próbujesz przypisać wartość do klucza, który obecnie nie istnieje w tabeli. Dzięki temu możesz przechwytywać i kontrolować modyfikacje, zanim do nich dojdzie.
Oto prosty przykład, który uniemożliwia dodawanie nowych kluczy do tabeli:
local readOnly = {}
local meta = {
__newindex = function(table, key, value)
print("Cannot add key: " .. key)
end
}
setmetatable(readOnly, meta)
readOnly.name = "Alice" -- Wynik: Cannot add key: name
print(readOnly.name) -- Wynik: nilGdy próbujesz ustawić readOnly.name = "Alice", Lua widzi, że klucz name nie istnieje w tabeli. Zamiast go dodać, Lua wywołuje funkcję __newindex z trzema argumentami: samą tabelą, ustawianym kluczem i przypisywaną wartością.
W tym przykładzie funkcja po prostu wyświetla komunikat i tak naprawdę niczego nie przechowuje.
Możesz też użyć __newindex, aby zweryfikować dane przed dodaniem ich do swojej tablicy:
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" -- Wynik: Health must be a number!
player.health = 100 -- Dozwolone, zapisane za pomocą rawset
player.health = "high" -- Brak komunikatu: health już istniejeZwróć uwagę na użycie rawset() w drugim przykładzie. Ta funkcja omija metatabelę i bezpośrednio ustawia wartość w tabeli.
Bez tego ustawienie wartości ponownie wywołałoby __newindex, tworząc nieskończoną pętlę.
Ponieważ __newindex uruchamia się tylko dla kluczy, których tabela nie zawiera, to sprawdzenie zabezpiecza pierwsze przypisanie każdego klucza. Gdy rawset zapisze już health, kolejne przypisania zmieniają tę wartość bezpośrednio. Aby sprawdzać każde przypisanie, pozostaw samą tabelę pustą i przechowuj wartości w drugiej tabeli, do której odwołuje się ona przez __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" -- Wynik: Health must be a number!
print(player.health) -- Wynik: 100player nigdy nie otrzymuje własnego klucza, więc każde przypisanie przechodzi przez __newindex, a każdy odczyt przechodzi przez __index do data.
Wyzwanie
ŁatwyNapisz funkcję createValidatedTable, która przyjmuje key i value oraz zwraca tabelę akceptującą wyłącznie wartości typu string dla nowych kluczy.
Utwórz pustą tabelę i dołącz do niej metatabelę z polem __newindex, które sprawdza typ wartości. Jeśli ktoś spróbuje dodać wartość inną niż string, użyj rawset(), aby zamiast niej zapisać string "invalid". Wartości typu string zapisuj normalnie za pomocą rawset().
Logika:
- Utwórz pustą tabelę
- Utwórz metatabelę z polem
__newindex, które otrzymuje tabelę, klucz i wartość - Wewnątrz
__newindexsprawdź, czy wartość jest typu string, używająctype(value) == "string" - Jeśli jest typu string, użyj
rawset(t, key, value), aby ją zapisać - Jeśli nie jest typu string, użyj
rawset(t, key, "invalid"), aby zamiast niej zapisać"invalid" - Dołącz metatabelę do tabeli za pomocą
setmetatable() - Użyj
rawset(), aby dodać początkową parę klucz-wartość do tabeli (z pominięciem metatabeli) - Zwróć tabelę
Parametry:
key(string): Początkowy klucz, który należy dodać do tabelivalue(string): Początkowa wartość, którą należy dodać do tabeli
Zwraca: Tabelę z walidacją, która akceptuje wyłącznie wartości typu string i zawiera początkową parę klucz-wartość (table)
Spróbuj swoich sił
function createValidatedTable(key, value)
-- Napisz kod tutaj
end
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Logika i przepływ programu
1Zaawansowane iterowanie po tabelach
Iterowanie za pomocą pairs()Iterowanie za pomocą ipairs()pairs() a ipairs()Powtórka — karta postaci2Więcej funkcji biblioteki table
table.concat()Tworzenie tabel i unpack()table.sort()Sortowanie niestandardowe za pomocą funkcjiPodsumowanie — tablica najlepszych wyników5Projekt: silnik tekstowej gry przygodowej
Konfiguracja projektu: pokójŁączenie pokoiPoćwicz samodzielnie: Kompilator Lua online