Vala中的类型系统简介

Vala语言知识介绍

Posted by wszqkzqk on February 7, 2023
本文字数:2726

Vala语言的类型系统较为多样,可能会让初学者感到困惑,但其实部分内容只是为了减小性能开销,表现上并没有太多区别(对于初学者只用struct和继承自GLib.Objectclass其实就行了)。笔者在此对Vala中的类型系统进行简单介绍,可以用来了解那些不常用的类在什么时候有用QwQ。

Vala中的类型

  • 继承自GLib.Object的类
  • 非对象类
  • 紧凑类
  • 不透明紧凑类

以及类似物:

  • 结构体struct

如上文所述,初学者没有必要全部掌握,只需要会使用继承自GLib.Object的类以及结构体struct即可,其他的则主要是从性能角度考虑,往往在功能上有所缺失,继承自GLib.Object的类的特性最为丰富。

主要区别因素

  • classstruct
    • Vala显式区分classstructstructclass在Vala中的本质区别struct在栈上调用,通过拷贝值进行传递,而class在堆上调用,通过指针进行传递
  • 是否在GObject类型系统中注册
  • 是否继承自GLib.Object基类

访问权限

Vala的访问权限修饰符与C#类似:

  • public: 没有访问限制
  • private: 访问仅限该类(在没有访问修饰符时默认)
  • protected: 访问仅限该类及继承自该类的子类
  • internal: 访问仅限同一个程序集

在某些为了性能而舍弃了一定完备性的类型系统中,部分访问修饰符不能使用。

结构体(struct

结构体在Vala中通过值进行传递,Vala的结构体下可以拥有字段,也可以拥有方法、属性,但结构体的字段的访问控制默认且只能公开(public,如果设置为其他修饰符,编译器会报出警告,并忽略这里的修饰符。但是,结构体的属性与方法仍然不受此限制,可以设定其访问修饰符。

结构体struct不是GObject类型系统的一部分。

类(class

类在Vala中通过指针进行传递,且在Vala中有多个种类。笔者在此逐一介绍。

继承自GLib.Object的类

一般使用的类型系统,支持Vala中类型系统的全部特性,但性能开销相对较大(一般情况下类创建/销毁并不是性能瓶颈,仍非常快)。定义时,必须要显式指明GLib.Object或者GLib.Object的子类继承,即使是从GLib.Object继承时class Foo : GLib.Object的基类指定仍然不可省略

非对象类

非对象类是不从GLib.Object继承,但仍然在GObject类型系统中注册的类,性能开销较继承自GLib.Object的类小不少。1非对象类仍能实现GObject类的大多数功能,但由于这样的类型并非GLib.Object的子类,利用了GLib.Object的特性均无法使用,因此非对象类不能使用GObject风格的构造,包括Object (...)construct构造语段,以及Object.new (type)的新建对象方式。

紧凑类与不透明紧凑类

紧凑类(包括不透明紧凑类)与struct类似,不在GObject类型系统中注册,特性较为受限,但紧凑类传递时仍然传递指针,与struct存在根本区别。

由于没有在类型系统中注册,紧凑类比非对象类的性能开销还要小很多(创建/销毁快约2.5倍),1是十分轻量的类型。另一方面,紧凑类因此也无法使用GObject提供的大量功能。紧凑类与不透明紧凑类的唯一区别在于,紧凑类中的所有字段的访问控制只能为publicinternal,而不透明紧凑类中的字段访问控制只能为privateinternal,这两种类型均不能混合私有与公开字段

定义紧凑类的方法是在class Foo { ... }前面加上[Compact]修饰符,类似地,定义不透明紧凑类则需要加[Compact (opaque = true)]修饰符。

笔者在此简要列出紧凑类的受限之处:

  • 与非对象类一样,不能使用GObject风格的构造
  • 不能实现接口
  • 紧凑类之间可以继承,但继承的紧凑类不能拥有新的字段
  • 默认不通过引用计数管理
    • 默认情况下紧凑类只能有一个强引用,在生命周期结束后自动销毁,其他引用只能是无属引用
      • 可以加入[Immutable]修饰符并用[CCode (copy_function = "foo_copy")]修饰符指定复制函数,实现强引用时自动拷贝
    • 也可以手动设置用于引用计数的函数来恢复引用计数(使用[CCode (ref_function = "foo_up", unref_function = "foo_down")]
  • 不能实现信号
  • 不能用is检测类型,也不能正确获取类型信息
  • 不能混合publicprivate修饰的字段,不能使用protected修饰的字段

由于诸多限制的存在,仅在语言绑定中或对性能有特殊需求的场景下才推荐使用紧凑类;由于紧凑类完全不依赖GObject对象系统,如果不希望程序链接到GObject库,也可以使用紧凑类取代依赖GObject中的类型。

归纳

特性 继承自GLib.Object的类 非对象类 紧凑类 不透明紧凑类 结构体
传递方式 指针引用 指针引用 指针引用 指针引用 值拷贝
字段访问修饰 不受限 不受限 publicinternal privateinternal 无效(均公开)
是否依赖GObject类型系统
GObject风格构建支持
运行时类型检测与类型信息获取
接口
默认引用计数
信号
继承 ●(不支持实现新字段) ●(不支持实现新字段)
抽象/虚方法
创建性能开销(越高开销越大)1 10 2.5 1 1 1
  1. Vala’s Memory Management Explained: Creating and destroying compact classes is faster than non-compact classes (about 2.5 times faster than regular classes, and an order of magnitude faster than GObject-derived classes), though other operations are no different. That said, modern hardware can create and destroy millions of GObject-derived classes per thread per second, so it is advisable to make sure this is a performance bottleneck before trying to optimize it away. 关于结构体需要注意的是,结构体的传递方式是值拷贝,因此虽然创建性能开销小,但是传递性能开销大。  2 3