Menu
CoddyTech

使用 setter 进行验证

本课属于 Coddy GDScript 学习旅程的“面向对象编程”单元。第 27 课,共 65 课。

每次给属性赋值时,setter 都会运行,因此这里适合检查新值。Python 会因无效值而引发错误。GDScript 没有异常,因此 setter 会采用以下做法之一:将值限制在有效范围内,或者保留旧值并记录出错原因。

# character.gd
class_name Character
extends RefCounted

var last_error: String = ""
var hp: int = 100:
	set(value):
		hp = clampi(value, 0, 100)
var nickname: String = "Player":
	set(value):
		if value.length() < 3:
			last_error = "Name too short"
			return
		last_error = ""
		nickname = value

clampi(value, 0, 100) 会将 value 调整到 0 到 100 的范围内。在 nickname 设置器中,return 会在赋值前退出,因此旧名称会保留,而 last_error 会说明原因:

# main.gd
var hero := Character.new()
hero.hp = 250
print(hero.hp) # 100
hero.nickname = "Al"
print(hero.nickname) # Player
print(hero.last_error) # Name too short
hero.nickname = "Alice"
print(hero.nickname) # Alice

在类内部进行赋值时,设置器也会运行,包括在 _init() 中以及每个方法中。通过属性赋值构造函数的参数,这些参数也会经过检查:

func _init(start_hp: int) -> void:
	hp = start_hp # 会运行 setter:Character.new(500) 的初始值为 100

在 setter 本身中,赋值给 hp 会存储该值,而不会再次调用 setter。唯一会跳过 setter 的赋值是声明中的初始值(= 100),所以这个值本身必须有效。

challenge icon

挑战

简单

为 thermostat.gd 中的 Thermostat 类添加带设置器的验证:

  • target(初始值为 20)只接受从 10 到 30 的值。对于其他任何值,它都会保留旧的 target,并将 last_error 设置为 Target must be between 10 and 30。
  • mode(初始值为 off)只接受 heat、cool 和 off。对于其他任何文本,它都会保留旧的 mode,并将 last_error 设置为 Unknown mode: ,后面接上该文本。
  • 有效赋值会将 last_error 重置为空字符串。
  • _init(start_target) 会将 start_target 赋给 target,因此会对它进行设置器检查。

给定的代码会读取起始目标值,然后读取命令数量,接着每行读取一条命令(target 25 或 mode cool)。创建 thermostat 后以及每条命令执行后,如果 last_error 不为空,代码就会打印 Rejected: 和错误信息,然后打印状态。起始值为 22,命令为 target 35 和 mode cool 时,输出如下:

Target 22, mode off
Rejected: Target must be between 10 and 30
Target 22, mode off
Target 22, mode cool

你的代码应写在 thermostat.gd 中。main.gd 包含给定的输入和输出代码,不能编辑。

亲手试试

extends Node

func _ready():
	# 提供的输入/输出代码:保持原样。
	var thermostat := Thermostat.new(int(OS.read_string_from_stdin()))
	report(thermostat)
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ")
		if parts[0] == "target":
			thermostat.target = int(parts[1])
		else:
			thermostat.mode = parts[1]
		report(thermostat)

func report(thermostat: Thermostat) -> void:
	if thermostat.last_error != "":
		print("Rejected: " + thermostat.last_error)
	print("Target %d, mode %s" % [thermostat.target, thermostat.mode])
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: GDScript 在线编译器