.NET程序员新方向 Ruby核心语法入门

本文的目的是为了找出为什么.NET程序员都想学习并使用Ruby,并探索Ruby语言的核心语法。

微软的IronRuby项目为Windows平台带来了强大的动态语言,Ruby编程语言是一个现代的,面向对象的基本语言,它的语法灵感来自Perl和Smalltalk语言,它是由一名日本人松本行弘(外号Matz)发明的,用他的话说,他是想发明一种语言比Perl更强大,同时比Python更面向对象的编程语言,在“http://www.linuxdevcenter.com/pub/a/linux/2001/11/29/ruby.html”有一篇对松本行弘专访文章,大家可以去看看。于是Ruby被设计为非常贴近自然语言,作者的原意就是要减少编程时候的不必要的琐碎时间,令编写程序的人高兴,他于1996年发布了1.0版本。

这么多年来,Ruby一直鲜为人知,但它的功能已经远远超出了最初设计时的想法:以最简化的方法操作数据和环境。我第一次“玩”它还是在几年前,那时我正在寻找一种替换处理自动管理任务的批处理文件的方法。

Ruby真正开始流行还得从一个来自伊利诺斯洲芝加哥市的名叫37signals小公司说起,它们发布了一个名叫Rails的Web应用程序框架,这个新的框架吸取了已经被证明是可靠的Model-View-Controller和ActiveRecord模型的经验,并且添加了一些新的思想,如convention over configuration,导致它实现了太多的目标,几乎不需要编码了。

RubyCLR和IronRuby

在2006年早些时候,John Lam发布了一个开源项目,叫做RubyCLR,它在Ruby和.NET之间起到一个桥梁的作用,它允许用户可以直接从Ruby访问.NET平台丰富的资源,甚至将Ruby对象都暴露给CLR了,这个项目非常有雄心,但它没有打算将Ruby向.NET靠拢,而是打算让这两个世界相互对话,你仍然需要在你的机器上按照Ruby运行时环境。

RubyCLR项目为人们理解如何将Ruby和.NET和谐地溶合到一起迈出了关键的第一步,John的工作没有引起人们的注意,2006年末,他在他的博客上宣布加入微软新成立的动态语言运行时环境(DLR)团队,在John宣布前几个月,微软发布了IronPython的1.0版本,它是Python语言在.NET框架上一个新的实现,动态语言运行时环境在IronPython上工作,它在.NET框架构建了一个运行环境,允许动态语言进入.NET。

John和他的团队在2007年的MIX大会上宣布了IronRuby,可能真正让人吃惊的是IronRuby项目本身是微软的第一个真正意义上的开源.NET语言,不仅可以得到源代码,而且还可以获取来自社区的贡献。

IronRuby仍然处于发展阶段,然而偶然也会删掉已经可以利用的东西,这些东西通常是其它项目的一部分,如最近发布的Silverlight 2.0 Beta 2,这些后续的项目也放在源代码树中了,并且也有相应的邮件列表。

为什么要学习Ruby?

我最喜欢的一本书叫做《程序员实务:从熟练工到大师》【英文名是《The Pragmatic Programmer: From Journeyman to Master》】,该书的作者鼓励程序员每年学习一门新的编程语言,对于我而言,当我学习了Ruby语言后,大大地改变了我的专业范围。

Ruby是一门完全面向对象的语言,这意味着在系统中每一样打交道的东西都是对象,包括直接的值,如数字,即使是类,也是由新创建的对象实例组成的模板。

因为Ruby是一个动态语言,你会发现类型已经变得不太重要了,当一个类函数以参数形式获取到一个对象时,不需要指定对象需要的类型。实际上,Ruby没有编译器,因此,可能直到传递给类函数的对象不满足方法的需要时,你才会发现这一点。

如果你象我几年前那样,你也许会发现这个概念让你不安,如果没有编译器,那么你可能要尽可能最快地在运行前就了解代码中的错误,而不用等到运行时才知道。如果你还是习惯于让编译器告诉你错误,那你就不用选择Ruby了。
正是由于以前编译器能够报告错误,如类型不匹配,当你编写一个类函数时,你可能希望“这里的对象必须能够做到foo和bar”,然后创建一个接口叫做IFooBar,看起来这是一个不错的解决方案,但当你想使用其它的在IfooBar之前创建的类时(特别是那些来自框架的类型),你就会失败了。

作者提醒:IronRuby还没有成为主流的工具,你可以使用Ruby的标准版本进行学习,如果你想实验后面的例子,可以从http://rubyinstaller.rubyforge.org/下载。

Ruby示例

学习Ruby或一门新的编程语言最好的方法就是多练习,研究它的交互接口,大多数动态语言都有交互提示符,称之为读-执行-打印环(即REPL,Read-Execute-Print Loop),Ruby中的REPL程序叫做irb(即交互式Ruby,interactive Ruby)。

当你执行irb程序时,你会看到一个irb提示符,如:

C:\Users\Brad> irb
irb(main):001:0>
 

当你在irb提示符后敲入命令时,Ruby解释程序就会评估它们,并将结果输出到你屏幕上,与irb类似的REPL是学习一门语言的优秀方法:每次一条语句。

下面对irb做一个简单的介绍,在irb提示符后,敲入5+2,并回车,告诉Ruby计算这个表达式的值:

irb(main):001:0> 5 + 2
=> 7
 

irb(main):001:0>部分是irb的提示符,当你敲入5+2并回车时,irb就将结果输出到屏幕上,如这里的=> 7,=> 是irb显示输出结果时使用的提示符。

如果Ruby认为你还没有完成表达式的书写,它允许你继续换行书写,如当你敲入5+2+时就按了回车,Ruby认为你还有一部分没有输入完毕,它会继续让你在下一行输入,如:

irb(main):002:0> 5 + 2 +
irb(main):003:0* 13
=> 20
 

第二行的提示符变为星号(*)了,而不是“>”,这样你就知道你在完成前面没有完成的表达式。

基础类型

如果一门编程语言不能处理数字,那就不值得学习和使用,Ruby当然能够满足算术运算了,如:

irb(main):004:0> 3 + 4
   => 7
   irb(main):005:0> 3 * 4
   => 12
   irb(main):006:0> 3 - 4
   => -1
   irb(main):007:0> 3 / 4
   => 0
   irb(main):008:0> 3.0 / 4.0
   => 0.75
   irb(main):009:0> 0xF
   => 15
   irb(main):010:0> 0x3 * 0xA
   => 30
 
正如你所看到的,Ruby支持整数和浮点类型,甚至可以接收常用的十六进制整数,但0x3 * 0xA的结果是以十进制的形式显示的,即显示结果是30而不是0x1E。

因为在.NET中,数字也是真实的对象,因此,你可以在它们上面调用类函数,如:

irb(main):011:0> 14.to_s
   => "14"

在c++中不要这样做。

to_s类函数的功能是将一个对象转换成一个字符串,因此,14.to_s返回的结果是"14",和.NET中的to_string()函数一样,to_s函数实际上是一个对象函数,因此,在Ruby中你可以将任何东西转换成字符串。

字符串

Ruby的字符串具备完整的操作支持,如:

irb(main):012:0> "hello" + "there"
  => "hellothere"
   irb(main):013:0> "Reader".length
   => 6
   irb(main):014:0> "Reader".reverse
   => "redaeR"
   irb(main):015:0> "reader".capitalize
   => "Reader"
   irb(main):016:0> "Reader".include?("foo")
   => false
   irb(main):017:0> "Reader".include?("ade")
   => true
   irb(main):018:0> "  Reader  ".strip
   => "Reader"
   irb(main):019:0> "Reader".gsub("e", "f")
   => "Rfadfr"
   irb(main):020:0> "Reader".delete("ea")
   => "Rdr"
   irb(main):021:0> "a" < "b"
   => true
 
几乎可以使用所有的字符串操作符,可能有的你还从来都没有使用过,如下面的代码按字母顺序测试某个字符串是否位于其他两个之间:

irb(main):022:0> "Bob".between? "Adam", "Chris"
   => true
 
乘法操作符可以让给定的字符串重复显示指定的数量,如:

irb(main):023:0> "hi" * 5
   => "hihihihihi"
 
Crypt函数为字符串提供了一个单向哈希加密功能,在存储敏感数据如密码时就可以使用它,如:

irb(main):024:0> "Reader".crypt("ab")
   => "abofgDjq6JNJo"
 
字符

Ruby没有内置的字符类型,它象数字一样表现字符,可以是?语法来表示一个字符常量,你可以使用chr函数将一个数字转换成一个等价的字符串,如:

irb(main):025:0> "Reader"[2]
   => 97
   irb(main):026:0> ?a
   => 97
   irb(main):027:0> 97.chr
   => "a"
 
赋值

其实执行这个操作并没什么用途,除非你可以将其存储起来方便后面使用,如:

irb(main):028:0>x = 42
   =>42
 
字符串有一个特殊的语法,允许嵌入式赋值,这个赋值不仅仅局限于简单的变量替换,它是一个完整的赋值,如:

irb(main):029:0> "The answer is #{x}!"
   => "The answer is 42!"
   irb(main):030:0> "The answer is #{6 * 7}!"
   => "The answer is 42!"
 
可以使用单引号将字符串引起来避免这种赋值,注意是单引号,不是双引号,如:

irb(main):031:0> 'The answer is #{x}!'
   => "The answer is \#{x}!"

 

数组

Ruby中的数组与.NET 1.0中的ArrayList类很接近,它们的大小都是可变的,用于存储任意类型的数据,从0开始编号,如:

irb(main):032:0> a = ["hello", 42, "world"]
   => ["hello", 42, "world"]
   irb(main):033:0> a << 5.0 * 7.5
   => ["hello", 42, "world", 37.5]
   irb(main):034:0> a[0]
   => "hello"
   irb(main):035:0> a[6] = 'hi' * 2
   => "hihi"
   irb(main):036:0> a
   => ["hello", 42, "world", 37.5, nil, nil, "hihi"]
   irb(main):037:0> a[99]
   => nil
 
前面的代码显示了如何使用<<操作符向数组末尾追加项目,以及获取或设置值使用的指针操作符[],当你向数组末尾添加一个项目时,Ruby使用零值填充数组中的“洞”,当你访问数组外的值时,Ruby返回零值而不是异常。

你可以使用一个范围的指针将数组分片,也可以使用负的指针从后向前访问数组,-1就是最后一项,-2是倒数第二项,以此类推,但不能使用反向范围获取反向分片,你可以使用一个正向范围,然后调用reverse方法,如:

irb(main):038:0> a[-1]
   => "hihi"
   irb(main):039:0> a[1..3]
 =>[42, "world", 37.5]
   irb(main):040:0>a[2..-2]
   =>["world", 37.5, nil, nil]
   irb(main):041:0>a[-4..-1]
   =>[37.5, nil, nil, "hihi"]
   irb(main):042:0>a[-1..-4]          # 不能工作
   =>[]
   irb(main):043:0>a[-4..-1].reverse  # 能够工作
   =>["hihi", nil, nil, 37.5]
 
和字符串一样,你会发现有多个唯一对数组有用的类函数,如:

irb(main):044:0> a
   => ["hello", 42, "world", 37.5, nil, nil, "hihi"]
   irb(main):045:0> a.compact
   => ["hello", 42, "world", 37.5, "hihi"]
   irb(main):046:0> a.join
   => "hello42world37.5hihi"
   irb(main):047:0> [10, 75, 6, 29].sort
   => [6, 10, 29, 75]
   irb(main):048:0> [[1, 2, 3], [4, 5, 6]]
   => [[1, 2, 3], [4, 5, 6]]
   irb(main):049:0> [[1, 2, 3], [4, 5, 6]].flatten
   => [1, 2, 3, 4, 5, 6]
 
散列

Ruby的最后一个核心数据结构是散列,与.NET 1.0中的散列表类似,它是一个联合数组,它的键值可以是任意类型的值,它们指向的数据也可以是任意类型的数据,实际上,大部分散列使用的是符号作为键值。

使用{}语法声明散列,并且使用key => value格式声明初始值,在散列中获取或设置值时都可以使用键值操作符,如:

irb(main):050:0> h = {:foo=>'bar', :baz=>'biff'}
   => {:foo=>"bar", :baz=>"biff"}
   irb(main):051:0> h[:foo]
   => "bar"
   irb(main):052:0> h[:unknown]
   => nil
   irb(main):053:0> h[:baz] = "new"
   => "new"
   => {:foo=>"bar", :baz=>"new"}
   irb(main):054:0> h.entries
   => [[:foo, "bar"], [:baz, "new"]]
 
变量

Ruby中的变量和类函数名都是以小写字母开头的,可以包括字母、数字和下划线。本地变量没有前缀,实例变量以@开头,全局变量以$开头。
在使用变量前无需声明,未初始化的变量有一个零值,下面是几个预定义的变量:

nil表示一个“无”对象,与.NET中的null类似,除了nil是一个实例化的NilClass类外。

true和false分别是实例化的TrueClass和FalseClass。

在类函数中使用时,self指向调用类函数的对象实例;在一个类中使用时,它指的是实例化的类对象本身。

__FILE__ 和__LINE__返回当前执行文件和那个文件中的行号。

符号

Ruby有一个特殊类型的字符串,叫做符号,因为字符串在Ruby中是可以被修改的,使用它们作为散列键是很慢的,而且有一些情况是不能预测的。
除了它们是以冒号(:)开头外,符号的命名规则和变量的命名规则一致,你不能改变符号的值,两个名字相同的符号它们的身份就一样,它们可以作为优秀的散列键,查找请求只需要比较整数值,而不是与一个可变长字符串的值进行对比。

Ruby中的所有事物都是对象,所有对象都是类的实例,为了探索类是个什么东西,在它上面调用类函数:

5.class
   => Fixnum
   (2 ** 96).class
   => Bignum
   7.5.class
   => Float
   (1..10).class
   => Range
   "foo".class
   => String
   /^foo[a-e]$/.class
   => Regexp
   :foo.class
   => Symbol
   [].class
   => Array
   {}.class
   => Hash
 
块和闭包

虽然这与.NET 1.X中的事件处理程序类似,但当你想处理它们时还是必须要定义完整的类函数来连接这些事件,这就导致需要创建大量的类函数,因为框架需要它。

.NET 2.0引入了匿名委派的概念,它们起的作用与Ruby中的块类似,如:

irb(main):001:0> h = {:foo=>'bar', :hi=>'there'}
   => {:foo=>"bar", :hi=>"there"}
   irb(main):002:0> h.each_key {|k| puts k}
   foo
   hi
   => {:foo=>"bar", :hi=>"there"}
   irb(main):003:0> h.each {|k,v| puts "#{k}: #{v}"}
   foo: bar
   hi: there
   => {:foo=>"bar", :hi=>"there"}
 
正如你所看到的,Ruby中块的语法是相当简洁的:通常使用一对大括号打开块和关闭块,使用|x,y|语法标出传递给块的变量。

Ruby中的块和闭包类似,正如.NET 2.0中的匿名委派,这意味着它们有权访问它们封装作用域的值,即使那个作用域退出后也可以访问。下面是一个将几个值相乘的闭包示例:

irb(main):004:0> n = [5, 6, 10]
   => [5, 6, 10]
   irb(main):005:0> t = 1
   => 1
   irb(main):006:0> n.each { |i| t *= i }
   => [5, 6, 10]
   irb(main):007:0> t
   => 300
 
你甚至可以将引用存储在块中,方便以后使用,如:

irb(main):008:0> t = 1
   => 1
   irb(main):009:0> f = lambda { |i| t *= i }
   => # 函数Ruby中函数的定义比.NET简单多了,因为不需要指定类型,如:

irb(main):001:0> def greet(name)
   irb(main):002:1>   puts "Hello, #{name}!"
   irb(main):003:1> end
   => nil
   irb(main):004:0> greet "Reader"
   Hello, Reader!
   => nil
   irb(main):005:0> greet 42
   Hello, 42!

 

Ruby执行的某些东西叫做“鸭式输入”:如果它走起路来像鸭子或声音也像鸭子,那它一定就是鸭子。你不用问它“你是一只鸭子吗?”,你只需要将它当做鸭子对它呷呷地叫就可以了,如果你渴望成为一只鸭子,只要你能呷呷地叫的就可以加入这个party。

注意greet函数定义时没有对对象的类型做任何限制,因为它只打印它们—Ruby中任何事物都是支持打印的,这得益于to_s类函数的优点,它可以将任何对象转换成字符串,最终结果就是你可以greet它们。

下面是另一个例子:

irb(main):006:0> def print_len(item)
   irb(main):007:1>   puts "Len = #{item.length}"
   irb(main):008:1> end
   => nil
   irb(main):009:0> print_len "Reader"
   Len = 6
   => nil
   irb(main):010:0> print_len [1, 4, 9]
   Len = 3
   => nil
   irb(main):011:0> print_len 42
   NoMethodError: undefined method <span class="pf">'</span>length' for
   42:Fixnum
           from (irb):7:in <span class="pf">'</span>print_len'
           from (irb):11

这里的print_len函数做的事情更多了:它调用了length函数。因此传递给print_len的是length函数的返回值,可以传递一个字符串或一个数组,因为它们都有对应的length函数,但是不能传递一个数字,因为没有对应数字的length函数。

为了在.NET中编写一个类似的函数,你可能需要创建一个IHaveLength接口作为你的参数类型,由于在你创建接口前类就已经创建好了,所以不幸的是,可能你需要创建一个类型转换器。

从另一方面来看,至少你已经有了IHaveLength,并且知道函数需要什么东西,既然类型担当的是某种格式文档的角色,在动态语言中你需要一个取舍,这样你会在编写文档和单元测试时更自信,可以帮助你识别类或函数是如何使用的。

Ruby支持默认的参数值,如:

irb(main):012:0>def repeat(val, times = 5)
   irb(main):013:1>val.to_s * times
   irb(main):014:1>end
   =>nil
   irb(main):015:0>repeat "hi"
   =>"hihihihihi"
   irb(main):016:0>repeat "hi", 3
   =>"hihihi"
   irb(main):017:0>repeat 10, 3
   =>"101010"
 
注意在to_s * times前面没有return,除非你明确地告诉它返回什么值,否则Ruby中的函数总是返回最后一个赋值,因此就不需要return关键字了。

Ruby支持变量参数,如:

irb(main):018:0> def add(*values)
   irb(main):019:1>   result = 0
   irb(main):020:1>   values.each {|x| result += x}
   irb(main):021:1>   result
   irb(main):022:1> end
   => nil
   irb(main):023:0> add 1, 2, 3, 4, 5
   => 15
 
Ruby将变量参数打包成一个数组,然后你就可以访问传递来的值,并且可以将它们集合到一块儿。

函数和变量命名约定

Ruby中的函数以小写字母开头,可以包含字母,数字和下划线。改变基础对象的函数名称以一个惊叹号(!)结束,例如:upcase函数返回字符串的大写,但是还单独保留了原始字符串;相反,upcase!函数就真实地改变了基础字符串。
回答问题(返回布尔值)的函数名称以一个问号(?)结束。

类是来自新对象实例创建时的模板,例如:为了将前面的greet函数放入一个类,你可能要编写以下代码:

irb(main):001:0> class Manners
   irb(main):002:1>   def greet(name)
   irb(main):003:2>     puts "Hello, #{name}!"
   irb(main):004:2>   end
   irb(main):005:1> end
   => nil
   irb(main):006:0> m = Manners.new
   => # 前面的代码创建了一个新的类,叫做Manners,并将函数greet添加到该类中了,最后,它创建了一个Manners类的实例,使用它greet Reader。

你可能认为在Ruby中类是对象的活动模板,与.NET中的类不同,Ruby中的类是编译时定义的,你可以对其进行任意扩展,当你完成扩展后,类的现有实例也会立即得到新的反应,注意当你尝试告诉它farewell时会发生什么,如:

irb(main):008:0> m.farewell "Reader"
   NoMethodError: undefined method 'farewell' for
   #<Manners:0x404839c>
           from (irb):8
 
当你尝试调用farewell时,系统会告诉你它不知道这是什么,那么就可以对Manners类进行扩展,让它知道这么说拜拜,如:

irb(main):009:0>class Manners
   irb(main):010:1>def farewell(name)
   irb(main):011:2>puts "Goodbye, #{name}!"
   irb(main):012:2>end
   irb(main):013:1>end
   =>nil
   irb(main):014:0>m.farewell "Reader"
   Goodbye, Reader!
   =>nil
 
扩展了Manners类后,它的已有实例就会立即获得这个新的功能。

Manners类有两个函数,两个都需要你的名字,你可能需要重新编写它以便你创建它时可以传递名字给它,Ruby调用initialize函数,你传递的所有参数都传递给new,下面是更新后的Manners类:

irb(main):001:0> class Manners
   irb(main):002:1>   def initialize(name)
   irb(main):003:2>     @name = name
   irb(main):004:2>   end
   irb(main):005:1>   def greet
   irb(main):006:2>     puts "Hello, #{@name}!"
   irb(main):007:2>   end
   irb(main):008:1>   def farewell
   irb(main):009:2>     puts "Goodbye, #{@name}!"
   irb(main):010:2>   end
   irb(main):011:1> end
   => nil
   irb(main):012:0> m = Manners.new "Reader"
   => #

注意类在一个实例变量@name中存储的名字,同时注意检查实例包括所有实例变量的值。

你自己定义的类可以随意扩展,而且也可以扩展Ruby内置的类,如:

irb(main):001:0> class Array
   irb(main):002:1>   def print_tr
   irb(main):003:2>     puts "<tr>"
   irb(main):004:2>     each { |item|
   irb(main):005:3*       puts "  <td>#{item}</td>"
   irb(main):006:3>     }
   irb(main):007:2>     puts "</tr>"
   irb(main):008:2>   end
   irb(main):009:1> end
   => nil
   Irb(main):010:0> ["hello","world!"].print_tr
   <tr>
     <td>hello</td>
     <td>world!</td>
   </tr>
   => nil
 
Rails对内置类型添加了许多扩展属性,提供了非常丰富的接口,例如:你可以编写类似5.days.from_now这样的代码,返回从现在开始5天后的日期。

迄今为止,你定义的函数都已经成为实例函数,即它们仅在类的实例中有效。Ruby也有静态函数,有时也叫做类函数。实际上,你已经调用过一次静态函数了,那就是new。

你可以在现有的类上添加任何新的静态函数,如:

irb(main):001:0> def String.concat(s1, s2)
   irb(main):002:1>   s1 + ' ' + s2
   irb(main):003:1> end
   => nil
   irb(main):004:0> String.concat 'hi', 'bye'
   => "hi bye"
 
你也可以使用self语法在定义或扩展类的上下文中定义它们,如:

irb(main):001:0> class String
   irb(main):002:1>   def self.concat(s1, s2)
   irb(main):003:2>     s1 + ' ' + s2
   irb(main):004:2>   end
   irb(main):005:1> end
   => nil
   irb(main):006:0> String.concat 'hi', 'bye'
   => "hi bye"
 
反射

反射是运行时发现关于对象的信息的过程,你可以通过调用methods函数找到某个类可用的函数,Ruby中的基础类也是对象,下面是查找在Ruby中对每个对象都有效的函数的代码:

irb(main):001:0> o = Object.new
   => #<Object:0x3f8feb4>
   irb(main):002:0> o.methods
   => ["inspect", "taguri", "clone", "public_methods"
   , "taguri=", "display", "instance_variable_defined
   ?", "equal?", "freeze", "methods", "respond_to?",
   ...many more methods listed...
 
调用methods函数返回的结果是一个字符串数组,包含了那个对象上有效的每个函数的名字,你也可以认为类与散列非常相似,调用methods函数就与获取散列表的键值相似。

你若不使用函数做点事情,你会觉得它很无趣,为了调用函数,你还可以使用send函数,如下面这两条语句都是等效的:

irb(main):003:0> o.inspect
   => "#<Object:0x3f8feb4>"
   irb(main):004:0> o.send "inspect"
   => "#<Object:0x3f8feb4>"
 
通过在你的类中定义method_missing函数,Ruby让你有机会处理未知的函数,如:

irb(main):139:0> class Object
   irb(main):140:1>   def method_missing(*args)
   irb(main):142:2>     puts args
   irb(main):143:2>   end
   irb(main):144:1> end
   => nil
   irb(main):145:0> o.foobar 1, 2, 3
   foobar
   1
   2
   3
   => nil
 
正如你所看到的,传递给method_missing函数的参数包括请求的函数和所有传递给那个函数的参数,一个更好的定义如下:

def method_missing(method, *args)

 

元编程

即使Ruby没有属性,你也可以使用函数调用,通常不需要括弧来模拟属性,你也需要影响Ruby以“=”结束函数的特殊处理方式,让它们担当调节器的作用。

你可以象下面这样定义一个person类:

irb(main):001:0> class Person
   irb(main):002:1>   def age
   irb(main):003:2>     @age
   irb(main):004:2>   end
   irb(main):005:1>   def age=(value)
   irb(main):006:2>     @age = value
   irb(main):007:2>   end
   irb(main):008:1> end
   => nil
 
接下来就可以使用person类的实例,将age当作person类的一个属性来处理,如:

irb(main):009:0>p = Person.new
   =># 如果你想将age的默认值设为一个非零的值,那么你可以使用initialize函数来设置。

这个代码显得非常标准,如果这是一个类似c#的语言,你可能会使用类似Visual Studio中片段,甚至静态代码的产生会自动生成reader和writer的属性。
在Ruby中,你可以使用元编程做一点努力就可以创建这些事物,理想情况下,你可以编写类似下面这样的代码:

class Person
     prop :age
   end

你应该在对象上定义个类(静态)函数以便你在定义自己的类时可以使用它,你也可以使用一个你还没有看到过的函数,class_eval函数,如:

irb(main):001:0> class Object
   irb(main):002:1>   def self.prop *names
   irb(main):003:2>     names.each { |name|
   irb(main):004:3*       self.class_eval "
   irb(main):005:3"         def #{name}
   irb(main):006:3"           @#{name}
   irb(main):007:3"         end"
   irb(main):008:3>       self.class_eval "
   irb(main):009:3"         def #{name}=(value)
   irb(main):010:3"           @#{name} = value
   irb(main):011:3"         end"
   irb(main):012:3>     }
   irb(main):013:2>     nil
   irb(main):014:2>   end
   irb(main):015:1> end
   => nil
 
上面使用的class_eval函数是创建了另外一个函数结束的,它给字符串赋值,因此你可以在你的类中编写自己的函数。

每个传递给prop函数的名字向新类添加了两个函数:getter和setter。最终使用你传递给prop的名字替换掉#{name}。

接下来,你可以在你的类定义中使用prop了,如:

irb(main):016:0> class Person
   irb(main):017:1>   prop :age, :name
   irb(main):018:1>
   irb(main):019:1*   def initialize(age, name)
   irb(main):020:2>     @age = age
   irb(main):021:2>     @name = name
   irb(main):022:2>   end
   irb(main):023:1> end
   => nil
   irb(main):024:0> p = Person.new(36, "Brad")
   => # 在你的环境中有了这些便利的工具后,你可以更快速地创建更高层次的类,使用这些元编程技巧可以帮助你工作得更好,不需要依赖于编辑片段或编译时代码生成。

小结

本文只是对Ruby中便利工具做了一个皮毛介绍,今天学习好Ruby可以在当Ruby.在.NET和Silverlight中可用时帮助你,有这么强大的一个动态编程语言,你的编程工具箱也会扩宽许多,但更重要的是,它可以帮助你开始以一种新的方式思考问题和解决方案。


 

posted @ 2008-11-20 23:18  剑者  阅读(292)  评论(0编辑  收藏  举报