`
datoplay
  • 浏览: 1611547 次
文章分类
社区版块
存档分类
最新评论

.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!
=> nil

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中可用时帮助你,有这么强大的一个动态编程语言,你的编程工具箱也会扩宽许多,但更重要的是,它可以帮助你开始以一种新的方式思考问题和解决方案。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics