Vala编程语言教程-属性
属性(Properties)
在面向对象编程中,向类使用者隐藏实现细节是良好的实践(信息隐藏原则),这样您后续可以修改内部实现而不会破坏公共API。一种常见的做法是将字段设为私有,并通过访问器方法(getters 和 setters)提供值的获取和设置。
如果您是Java程序员,可能会想到这样的实现:
class Person : Object {
private int age = 32;
public int get_age() {
return this.age;
}
public void set_age(int age) {
this.age = age;
}
}
这种方式可行,但Vala提供了更好的解决方案。问题在于这些方法使用起来较为繁琐。假设您想将某人的年龄增加一岁:
var alice = new Person();
alice.set_age(alice.get_age() + 1);
这正是属性(Properties)发挥作用的地方:
class Person : Object {
private int _age = 32; // 使用下划线前缀避免与属性名冲突
/* 属性声明 */
public int age {
get { return _age; }
set { _age = value; }
}
}
这种语法对C#程序员来说应该很熟悉。属性包含 get
和 set
代码块来获取和设置值,value
是表示新值的保留字。
现在您可以像访问公共字段一样使用属性,但实际执行的是 get
和 set
代码块中的逻辑:
var alice = new Person();
alice.age = alice.age + 1; // 更简洁的写法:
alice.age++;
如果只需要标准实现(如上述示例),您可以用更简洁的方式声明属性:
class Person : Object {
/* 带标准getter/setter和默认值的属性 */
public int age { get; set; default = 32; }
}
通过属性,您可以在不改变公共API的情况下修改类的内部实现。例如:
static int current_year = 2525;
class Person : Object {
private int year_of_birth = 2493;
public int age {
get { return current_year - year_of_birth; }
set { year_of_birth = current_year - value; }
}
}
这次年龄是通过出生年份实时计算的。请注意,get
和 set
代码块中不仅可以进行简单的变量访问,还可以实现数据库操作、日志记录、缓存更新等复杂逻辑。
只读属性
若要将属性设为只读,可以将setter设为私有:
public int age { get; private set; default = 32; }
或者直接省略 set
代码块:
class Person : Object {
private int _age = 32;
public int age {
get { return _age; }
}
}
属性描述
属性可以包含简短的昵称(nick)和详细描述(blurb),通过特殊属性标注:
[Description(nick = "年龄(年)", blurb = "此人的年龄,以年为单位")]
public int age { get; set; default = 32; }
这些描述可在运行时被查询。某些程序(如Glade图形界面设计器)会利用这些信息,为GTK+控件的属性提供人类可读的描述。
属性变更通知
所有继承自 GLib.Object
的类实例都包含名为 notify
的信号。当对象的任何属性发生变化时,该信号会被触发:
obj.notify.connect((s, p) => {
stdout.printf("属性 '%s' 已变更!\n", p.name);
});
其中 s
是信号源(本例中的obj),p
是类型为 ParamSpec
的属性元数据。若只需监听单个属性的变更:
lice.notify["age"].connect((s, p) => { stdout.printf("年龄已变更\n"); });
注意:属性名需使用连字符形式(如 my_property
应写为 "my-property"
),这是GObject的属性命名约定。
禁用变更通知
通过 CCode
属性标签可禁用特定属性的变更通知:
public class MyObject : Object {
[CCode(notify = false)]
// 此属性变更时不会触发通知信号
public int without_notification { get; set; }
// 此属性变更时会触发通知信号
public int with_notification { get; set; }
}
结构体类型的属性
重要提示:当属性为结构体类型时,必须按以下方式声明变量才能通过 Object.get()
获取值:
struct Color
{
public uint32 argb;
public Color() { argb = 0x12345678; }
}
class Shape: GLib.Object
{
public Color c { get; set; default = Color(); }
}
int main()
{
Color? c = null;
Shape s = new Shape();
s.get("c", out c);
}
这意味着此时 c
成为一个引用(reference),而非栈(stack)上的 Color 实例。此时传递给 s.get()
的实际上是 Color**
(二级指针)而非 Color*
(一级指针)。