分享

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

 燮羽 2011-04-22

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

    微软的IronRuby项目为Windows平台带来了强大的动态语言,Ruby编程语言是一个现代的,面向对象的基本语言,它的语法灵感来自Perl和Smalltalk语言,它是由一名日本人松本行弘(外号Matz)发明的,用他的话说,他是想发明一种语言比Perl更强大,同时比Python更面向对象的编程语言,在“http://www./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./下载。

    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 }
                          => #                    < PRE>
                          

      函数

      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
                          => #< PRE irb(main):007:0> m.greet ?Reader? Hello, Reader!=">" nil>

      前面的代码创建了一个新的类,叫做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"
                                => #< PRE m.greet Hello, Reader!=">" nil @name="Reader" > irb(main):013:0> irb(main):014:0> m.farewell Goodbye,>


      注意类在一个实例变量@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
                          =>#                    < PRE irb(main):011:0>p.age="42" =">nil" irb(main):012:0>p.age=">42">
                          

      如果你想将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")
                          => #
                          < PRE @name="Brad" > @age="36," irb(main):025:0> p.age=">" 36 irb(main):026:0> p.name=">" ?Brad?>
                          

      在你的环境中有了这些便利的工具后,你可以更快速地创建更高层次的类,使用这些元编程技巧可以帮助你工作得更好,不需要依赖于编辑片段或编译时代码生成。

      小结

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


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多