这是迄今为止我发现覆盖类中默认字段值的最佳方法:
(define sa-canvas%
(class canvas%
. . .
(init-field [min-width 600]
[min-height 300]
[stretchable-width #f]
[stretchable-height #f])
(super-new [min-width min-width]
[min-height min-height]
[stretchable-width stretchable-width]
[stretchable-height stretchable-height])
. . .))
对于Racket来说,这似乎太冗长了。 Racket的班级系统旨在支持的预期方式是什么?
答案 0 :(得分:2)
首先,您的示例做了您可能不想要的事情。使用init-field
在您的类的实例上创建一个可变的公共字段,并将其初始化为相应的初始化参数。这些方法并不经常使用:使用私有字段(即define
)和getter方法更为常见,如果您想支持突变,则可以使用setter方法。实际上,我们可以看出,尽管canvas%
使用了这些初始化参数,但它不会将它们存储为公共字段,因为(与方法一样)类无法创建与外部名称相同的新字段它的超类领域。 (可以使用inherit-field
来继承字段。)例如,评估此表达式会引发异常:
(class (class object%
(init-field [x 1])
(super-new))
(init-field [x 2])
(super-new [x x]))
对于不想作为字段保留的初始化参数,请使用init
而不是init-field
。此表达式与您的示例相同,但是不创建字段:
(class canvas%
(init [min-width 600]
[min-height 300]
[stretchable-width #f]
[stretchable-height #f])
(super-new [min-width min-width]
[min-height min-height]
[stretchable-width stretchable-width]
[stretchable-height stretchable-height]))
不过,您说的对,这是很多打字和需要保持同步的代码段。类库中没有内置的其他方法,但是您可以轻松地通过一个小宏添加自己:
(class canvas%
(define-syntax-rule (init/super-new [name default] ...)
(begin (init [name default] ...)
(super-new [name name] ...)))
(init/super-new
[min-width 600]
[min-height 300]
[stretchable-width #f]
[stretchable-height #f]))
define-syntax-rule
可以很容易地进入模块级别,但是:
本地宏很棒。和
关于宏的一大优点是您不必处理一般情况:例如,有时您可能还希望将其他参数传递给super-new
。如果您愿意,您当然可以支持,但是,有了宏,您和库编写者都不必预见您可能想要的所有可能的便利。编写一个简单的宏以使您的特定代码更具可读性是完全合法的。