Ruby 注解
2006 年 10 月 26 日
Ruby 最受欢迎的特性之一是它对 元编程 的支持,即像改变语言本身一样的特性 - 引入像新关键字这样的东西。
虽然主流的带花括号的语言对元编程的支持总体上较少,但它们确实有一个有用的特性,即 注解。注解是语言增强 InternalDslStyle 的重要功能。乍一看,Ruby 不支持注解,但如果你歪着头看,你会发现它确实支持。
使用注解,你可以用一个标记来标记语言元素(类、方法、字段等) - 本质上是关于该元素的元数据。然后,你可以在运行时或编译时使用这些元数据。
一个很好的例子是 NUnit2 开创的测试标记。
[Test] public void SomeTestMethod() {...
在运行时,NUnit 框架会查看类,找到用 [Test]
注解的方法,并运行它们。
注解可以带参数。因此,如果你对 Pascal 的子范围感兴趣,它允许你为变量指定有效范围,你可以用类似这样的东西来定义它们
@ValidRange(lower = 1, upper = 1000) private int weight; // in lb @ValidRange(lower = 1, upper = 120) private int height; // in inches
这与 Pascal 子范围的概念并不完全相同,这仅仅定义了有效范围,你仍然需要构建机制来检查值,例如在你的对象上调用 isValid
方法(我同意这并不是一个很好的 ContextualValidation)。但关键是,你已经定义了关于该变量的自己的声明性语句。
那么如何在 Ruby 中做到这一点呢?类似这样
validate_range :@height, :with => 1..120 validate_range :@weight, :with => 1..1000
在语法方面,最大的区别是,在 Java(或 .NET)中,你将注解放在你要注解的元素的前面。在 Ruby 中,你在注解中命名你要注解的元素。虽然这增加了一些输入(对于 Ruby 来说是一个罕见的声明),但它确实让你可以自由地在你的类中的任何地方放置你的注解。它也更容易构建引用多个语言元素的注解。
两种风格之间更深层的区别在于它们是如何实现的。带花括号的注解是一种特殊的语言结构,它将特殊对象作为元数据附加到语言元素。这些注解对象可以在编译时(使用 Java 的 apt)或运行时进行查询和处理。
另一方面,Ruby 的注解是通常在超类或包含模块中定义的类方法。通过直接在类定义中编写它们,它们在类加载时执行。因此,它们不是一个特定的语言特性,而是一种使用类方法的方式。你不需要创建元数据对象(尽管如果你想的话可以),更有可能的是,你只会构建对象来执行你想要它们执行的任务。
由于 Ruby 的动态特性,你可以用注解做更多有趣的事情。特别是,你可以进行代码生成。最明显的例子是像 attr_accessor :height
这样的东西,它将为字段生成 get 和 set 方法,有效地修改类本身。使用 apt,Java 可以做一些类似的事情,但你不能修改类本身。使用构建脚本和部分类,你可能可以在 C# 中做类似的事情,但这种运行时代码生成在 Ruby 中无疑更自然,它是它的 Lisp 式元素之一。
Ruby 程序员不称这些东西为注解。我喜欢做的事情之一是找到跨语言的通用技术,对我来说,这是一种通用技术,而“注解”似乎是一个很好的通用词。我不知道 Ruby 程序员是否会同意。