动态分发
程序总是使用静态调度。对于动态调度,用 method 替换 proc 关键字:
- type
- Expression = ref object of RootObj ## abstract base class for an expression
- Literal = ref object of Expression
- x: int
- PlusExpr = ref object of Expression
- a, b: Expression
- # 注意:'eval'依赖于动态绑定
- method eval(e: Expression): int {.base.} =
- # 重写基方法
- quit "to override!"
- method eval(e: Literal): int = e.x
- method eval(e: PlusExpr): int = eval(e.a) + eval(e.b)
- proc newLit(x: int): Literal = Literal(x: x)
- proc newPlus(a, b: Expression): PlusExpr = PlusExpr(a: a, b: b)
- echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
请注意,在示例中,构造函数 newLit 和 newPlus 是procs,因为它们使用静态绑定更有意义,但 eval 是一种方法,因为它需要动态绑定。
注意: 从Nim 0.20开始,要使用多方法,必须在编译时明确传递 —multimethods:on 。
在多方法中,所有具有对象类型的参数都用于分发:
- type
- Thing = ref object of RootObj
- Unit = ref object of Thing
- x: int
- method collide(a, b: Thing) {.inline.} =
- quit "to override!"
- method collide(a: Thing, b: Unit) {.inline.} =
- echo "1"
- method collide(a: Unit, b: Thing) {.inline.} =
- echo "2"
- var a, b: Unit
- new a
- new b
- collide(a, b) # output: 2
如示例所示,多方法的调用不能有歧义:collide2比collide1更受欢迎,因为解析是从左到右的。因此 Unit,Thing 比 Thing,Unit 更准确。
性能说明: Nim不会生成虚函数表,但会生成调度树。这避免了方法调用的昂贵间接分支并启用内联。但是,其他优化(如编译时评估或死代码消除)不适用于方法。