认识 6 个被误解的 Ruby 特性
openkk 12年前
<p> 如果您是一名 <code>C++</code> 程序员且需要在 Ruby 环境中工作,那么您有一些功课要做。本文讨论了 Ruby 新手可能会误解的六个 Ruby 特性,特别是当他或她来自一个类似但又不太相同的环境,比如 <code>C++</code>:</p> <ul> <li>Ruby 类层次结构</li> <li>Ruby 中的单例方法</li> <li><code>self</code> 关键词</li> <li><code>method_missing</code> 方法</li> <li>异常处理</li> <li>线程</li> </ul> <p> <strong>注意:</strong>本文中所有的代码均进行测试,且基于 Ruby 版本 1.8.7。</p> <p> Ruby 中的类层次结构</p> <p> Ruby 中的类层次结构会很棘手。创建一个 <code>Cat</code> 类型的类并开始探讨其层次结构(参见清单 1)。</p> <p> <strong>清单 1. Ruby 中的隐式类层次结构</strong></p> <div class="cnblogs_code"> <pre>irb (main):092:0> <span style="color:#0000ff;">class</span> Cat irb (main):093:1> end => nil irb (main):087:0> c = Cat.new => <span style="color:#008000;">#</span><span style="color:#008000;"><Cat:0x2bacb68></span><span style="color:#008000;"> </span>irb (main):088:0> c.<span style="color:#0000ff;">class</span> => Cat irb (main):089:0> c.<span style="color:#0000ff;">class</span>.superclass => Object irb (main):090:0> c.<span style="color:#0000ff;">class</span>.superclass.superclass => nil irb (main):091:0> c.<span style="color:#0000ff;">class</span>.superclass.superclass.superclass NoMethodError: undefined method `superclass<span style="color:#800000;">'</span><span style="color:#800000;"> for nil:NilClass</span><span style="color:#800000;"> </span> <span style="color:#0000ff;">from</span> (irb):91 <span style="color:#0000ff;">from</span> :0</pre> </div> <p><strong><br /> </strong> Ruby 中的所有对象(甚至用户定义的对象)都是 <code>Object</code> 类的后代,这在清单 1 中清晰可见。这与 <code>C++</code> 是鲜明的对比。这一点也不像普通数据类型,例如 <code>C/C++ int</code> 或 <code>double</code>。清单 2 显示了整数 1 的类层次结构。</p> <p> <strong>清单 2. 整数 1 的类层次结构</strong></p> <div class="cnblogs_code"> <pre>irb (main):100:0> 1.<span style="color:#0000ff;">class</span> => Fixnum irb (main):101:0> 1.<span style="color:#0000ff;">class</span>.superclass => Integer irb (main):102:0> 1.<span style="color:#0000ff;">class</span>.superclass.superclass => Numeric irb (main):103:0> 1.<span style="color:#0000ff;">class</span>.superclass.superclass.superclass => Object</pre> </div> <p> 到目前为止一切顺利。现在您知道了类本身是 <code>Class</code> 类型的对象。而 <code>Class</code> 最终派生自 <code>Object</code>,如清单 3 中所示使用 Ruby 内置的 <code>String</code> 类。</p> <p> <strong>清单 3. 类的类层次结构</strong></p> <div class="cnblogs_code"> <pre>irb (main):100:0> String.<span style="color:#0000ff;">class</span> => Class irb (main):101:0> String.<span style="color:#0000ff;">class</span>.superclass => Module irb (main):102:0> String.<span style="color:#0000ff;">class</span>.superclass.superclass => Object</pre> </div> <p> <code>Module</code> 是 <code>Class</code> 的基类,但是使用它时有一点要注意,即您不能直接实例化用户定义的 <code>Module</code> 对象。如果您不想深入 Ruby 内部,最好考虑与 <code>C++</code> 命名空间有类似特征的 <code>Module</code>:您可以定义您自己的方法、常量、等等。您在 <code>Class</code> 中包含了一个 <code>Module</code>,以及 voilà,<code>Module</code> 的所有元素现在会魔法般地成为 <code>Class</code> 的元素。清单 4 提供了一个示例。</p> <p> <strong>清单 4. Module 不能进行直接实例化,并且只能与类一同使用</strong></p> <div class="cnblogs_code"> <pre>irb (main):020:0> module MyModule irb (main):021:1> <span style="color:#0000ff;">def</span> hello irb (main):022:2> puts <span style="color:#800000;">"</span><span style="color:#800000;">Hello World</span><span style="color:#800000;">"</span> irb (main):023:2> end irb (main):024:1> end irb (main):025:0> test = MyModule.new NoMethodError: undefined method `new<span style="color:#800000;">'</span><span style="color:#800000;"> for MyModule:Module</span><span style="color:#800000;"> </span> <span style="color:#0000ff;">from</span> (irb):25 irb (main):026:0> <span style="color:#0000ff;">class</span> MyClass irb (main):027:1> include MyModule irb (main):028:1> end => MyClass irb (main):029:0> test = MyClass.new => <span style="color:#008000;">#</span><span style="color:#008000;"><MyClass:0x2c18bc8></span><span style="color:#008000;"> </span>irb (main):030:0> test.hello Hello World => nil</pre> </div> <p> 下面再重申一下重点:当您使用 Ruby 编写 <code>c = Cat.new</code> 时,<code>c</code> 是派生自 <code>Object</code> 的 <code>Cat</code> 类型的一个对象。<code>Cat</code> 类是 <code>Class</code> 类型的一个对象,Class 派生自 <code>Module</code>,而 Module 又派生自 <code>Object</code>。因此该对象及其类型都是有效的 Ruby 对象。</p> <p> 单例方法和可编辑类</p> <p> 现在,看一下单例方法。假设您想使用 <code>C++</code> 建模类似于人类社会的东西。那么您会如何做呢?定义一个名为 <code>Human</code> 的类,然后定义数百万的 <code>Human</code> 对象?这更像是在建模一个呆板的社会;每个人必须具惟一的特征。Ruby 的单例方法在这里就派上了用场,如清单 5 所示。</p> <p> <strong>清单 5. Ruby 中的单例方法</strong></p> <div class="cnblogs_code"> <pre>irb (main):113:0> y = Human.new => <span style="color:#008000;">#</span><span style="color:#008000;"><Human:0x319b6f0></span><span style="color:#008000;"> </span>irb (main):114:0> <span style="color:#0000ff;">def</span> y.paint irb (main):115:1> puts <span style="color:#800000;">"</span><span style="color:#800000;">Can paint</span><span style="color:#800000;">"</span> irb (main):116:1> end => nil irb (main):117:0> y.paint Can paint => nil irb (main):118:0> z = Human.new => <span style="color:#008000;">#</span><span style="color:#008000;"><Human:0x3153fc0></span><span style="color:#008000;"> </span>irb (main):119:0> z.paint NoMethodError: undefined method `paint<span style="color:#800000;">'</span><span style="color:#800000;"> for #<Human:0x3153fc0></span><span style="color:#800000;"> </span> <span style="color:#0000ff;">from</span> (irb):119</pre> </div> <p> Ruby 中的<em>单例方法</em> 是仅与特定对象关联的方法,不能用于一般的类。它们的前缀是对象名称。在清单 5 中,<code>paint</code> 方法特定于 <code>y</code> 对象,而且仅限于 <code>y</code> 对象;<code>z.paint</code> 导致一个 “方法未定义” 错误。您可以调用 <code>singleton_methods</code> 来查明一个对象中的单例方法列表:</p> <div class="cnblogs_code"> <pre>irb (main):120:0> y.singleton_methods => [<span style="color:#800000;">"</span><span style="color:#800000;">paint</span><span style="color:#800000;">"</span>]</pre> </div> <p> 不过在 Ruby 中有另一种定义单例方法的方式。看看清单 6 中的代码。</p> <p> <strong>清单 6. 创建单例方法的另一种方式</strong></p> <div class="cnblogs_code"> <pre>irb (main):113:0> y = Human.new => <span style="color:#008000;">#</span><span style="color:#008000;"><Human:0x319b6f0></span><span style="color:#008000;"> </span>irb (main):114:0> <span style="color:#0000ff;">class</span> << y irb (main):115:1> <span style="color:#0000ff;">def</span> sing irb (main):116:1> puts <span style="color:#800000;">"</span><span style="color:#800000;">Can sing</span><span style="color:#800000;">"</span> irb (main):117:1> end irb (main):118:1>end => nil irb (main):117:0> y.sing Can sing => nil</pre> </div> <p> 清单 5 还开创了新的可能性,可以添加新方法到用户定义的类和内置的 Ruby 现有类,比如 <code>String</code>。这在 <code>C++</code> 中是不可能实现的,除非您能够访问您使用的类的源代码。再次观察 <code>String</code> 类(清单 7)。</p> <p> <a name="list7"></a><strong>清单 7. Ruby 允许您修改一个现有的类</strong></p> <div class="cnblogs_code"> <pre>irb (main):035:0> y = String.new (<span style="color:#800000;">"</span><span style="color:#800000;">racecar</span><span style="color:#800000;">"</span>) => <span style="color:#800000;">"</span><span style="color:#800000;">racecar</span><span style="color:#800000;">"</span> irb (main):036:0> y.methods.grep (/palindrome/) => [ ] irb (main):037:0> <span style="color:#0000ff;">class</span> String irb (main):038:1> <span style="color:#0000ff;">def</span> palindrome? irb (main):039:2> self == self.reverse irb (main):040:2> end irb (main):041:1> end irb (main):050:0> y.palindrome? => true</pre> </div> <p> 清单 7 清楚地展示了如何编辑一个现有的 Ruby 类来添加您自行选择的方法。这里,我添加了 <code>palindrome?</code> 方法到 <code>String</code> 类。因此 Ruby 类在运行时是可编辑的(一个强大的属性)。</p> <p> 现在您对 Ruby 的类层次结构和单例有了一定的认识,接下来我们来看 <code>self</code>。注意,在定义 <code>palindrome?</code> 方法时我使用了 <code>self</code>。</p> <p> <a name="self"></a>发现 self</p> <p> <code>self</code> 关键词的最常见用法可能就是在 Ruby 类中声明一个静态方法,如清单 8 所示。</p> <p> <a name="list8"></a><strong>清单 8. 使用 self 声明类的静态方法</strong></p> <div class="cnblogs_code"> <pre><span style="color:#0000ff;">class</span> SelfTest <span style="color:#0000ff;">def</span> self.test puts <span style="color:#800000;">"</span><span style="color:#800000;">Hello World with self!</span><span style="color:#800000;">"</span> end end<span style="color:#0000ff;">class</span> SelfTest2 <span style="color:#0000ff;">def</span> test puts <span style="color:#800000;">"</span><span style="color:#800000;">This is not a class static method</span><span style="color:#800000;">"</span> end end SelfTest.test SelfTest2.test</pre> </div> <p> 从清单 8 的输出中可以看到(如清单 9 所示),没有对象您无法调用非静态方法。该行为类似于 <code>C++</code>。</p> <p> <a name="list9"></a><strong>清单 9. 在没有对象的情况下调用非静态方法时会出错</strong></p> <div class="cnblogs_code"> <pre>irb (main):087:0> SelfTest.test Hello World with self! => nil irb (main):088:0> SelfTest2.test NoMethodError: undefined method <span style="color:#800000;">'</span><span style="color:#800000;">test</span><span style="color:#800000;">'</span> <span style="color:#0000ff;">for</span> SelfTest2:Class <span style="color:#0000ff;">from</span> (irb):88</pre> </div> <p> 在探讨 <code>self</code> 更深奥的用途和含义之前,注意您也可以通过在方法名称前面加上类名来在 Ruby 中定义一个静态方法:</p> <div class="cnblogs_code"> <pre><span style="color:#0000ff;">class</span> TestMe <span style="color:#0000ff;">def</span> TestMe.test puts <span style="color:#800000;">"</span><span style="color:#800000;">Yet another static member function</span><span style="color:#800000;">"</span> end end TestMe.test <span style="color:#008000;">#</span><span style="color:#008000;"> works fine</span></pre> </div> <p><br /> 清单 10 提供了 <code>self</code> 的一个更有趣但不太容易找到的用法。</p> <p> <a name="list10"></a><strong>清单 10. 使用元类来声明静态方法</strong></p> <div class="cnblogs_code"> <pre><span style="color:#0000ff;">class</span> MyTest <span style="color:#0000ff;">class</span> << self <span style="color:#0000ff;">def</span> test puts <span style="color:#800000;">"</span><span style="color:#800000;">This is a class static method</span><span style="color:#800000;">"</span> end end end MyTest.test <span style="color:#008000;">#</span><span style="color:#008000;"> works fine </span></pre> </div> <p> 该段代码以一种稍微不同的方式将 <code>test</code> 定义为一个类静态方法。要了解究竟发生了什么,您需要看一下 <code>class << self</code> 语法的一些细节。<code>class << self … end</code> 创建一个元类。在方法查找链中,在访问对象的基类之前先搜索该对象的元类。如果您在元类中定义一个方法,可以在类上调用该方法。这类似于 <code>C++</code> 中静态方法的概念。</p> <p> 可以访问一个元类吗?是的:只需从 <code>class << self … end</code> 内返回 <code>self</code>。注意,在一个 Ruby 类声明中,您没有义务仅给出方法定义。清单 11 显示了元类。</p> <p> <a name="list11"></a><strong>清单 11. 理解元类</strong></p> <div class="cnblogs_code"> <pre>irb (main):198:0> <span style="color:#0000ff;">class</span> MyTest irb (main):199:1> end => nil irb (main):200:0> y = MyTest.new => <span style="color:#008000;">#</span><span style="color:#008000;">< MyTest:0x2d43fe0></span><span style="color:#008000;"> </span>irb (main):201:0> z = <span style="color:#0000ff;">class</span> MyTest irb (main):202:1> <span style="color:#0000ff;">class</span> << self irb (main):203:2> self irb (main):204:2> end irb (main):205:1> end => <span style="color:#008000;">#</span><span style="color:#008000;"><Class: MyTest > </span><span style="color:#008000;"> </span>irb (main):206:0> z.<span style="color:#0000ff;">class</span> => Class irb (main):207:0> y.<span style="color:#0000ff;">class</span> => MyTest</pre> </div> <p> 回到清单 7 的代码,您会看到 <code>palindrome</code> 被定义为 <code>self == self.reverse</code>。在该上下文中,<code>self</code> 与 <code>C++</code> 没有什么区别。<code>C++</code> 和 Ruby 中的方法都需要一个操作对象,以修改或提取状态信息。<code>self</code> 是指这里的这个对象。注意,可以通过附加 <code>self</code> 前缀来选择性地调用公共方法,指明方法付诸作用的对象,如清单 12 所示。</p> <p> <a name="list12"></a><strong>清单 12. 使用 self 调用方法</strong></p> <div class="cnblogs_code"> <pre>irb (main):094:0> <span style="color:#0000ff;">class</span> SelfTest3 irb (main):095:1> <span style="color:#0000ff;">def</span> foo irb (main):096:2> self.bar () irb (main):097:2> end irb (main):098:1> <span style="color:#0000ff;">def</span> bar irb (main):099:2> puts <span style="color:#800000;">"</span><span style="color:#800000;">Testing Self</span><span style="color:#800000;">"</span> irb (main):100:2> end irb (main):101:1> end => nil irb (main):102:0> test = SelfTest3.new => <span style="color:#008000;">#</span><span style="color:#008000;"><SelfTest3:0x2d15750></span><span style="color:#008000;"> </span>irb (main):103:0> test.foo Testing Self => nil</pre> </div> <p> 在 Ruby 中您无法通过附加 <code>self</code> 关键词前缀来调用私有方法。对于一名 <code>C++</code> 开发人员,这可能会有点混淆。清单 13 中的代码明确表示,<code>self</code> 不能用于私有方法:对私有方法的调用只能针对隐式对象。</p> <p> <a name="list13"></a><strong>清单 13. self 不能用于私有方法调用</strong></p> <div class="cnblogs_code"> <pre>irb (main):110:0> <span style="color:#0000ff;">class</span> SelfTest4 irb (main):111:1> <span style="color:#0000ff;">def</span> method1 irb (main):112:2> self.method2 irb (main):113:2> end irb (main):114:1> <span style="color:#0000ff;">def</span> method3 irb (main):115:2> method2 irb (main):116:2> end irb (main):117:1> private irb (main):118:1> <span style="color:#0000ff;">def</span> method2 irb (main):119:2> puts <span style="color:#800000;">"</span><span style="color:#800000;">Inside private method</span><span style="color:#800000;">"</span> irb (main):120:2> end irb (main):121:1> end => nil irb (main):122:0> y = SelfTest4.new => <span style="color:#008000;">#</span><span style="color:#008000;"><SelfTest4:0x2c13d80></span><span style="color:#008000;"> </span>irb (main):123:0> y.method1 NoMethodError: private method `method2<span style="color:#800000;">'</span><span style="color:#800000;"> called for #<SelfTest4:0x2c13d80></span><span style="color:#800000;"> </span> <span style="color:#0000ff;">from</span> (irb):112:<span style="color:#0000ff;">in</span> `method1<span style="color:#800000;">'</span>irb (main):124:0> y.method3 Inside private method => nil</pre> </div> <p> 由于 Ruby 中的一切都是对象,当在 <code>irb</code> 提示符上调用 <code>self</code> 时您会得到以下结果:</p> <div class="cnblogs_code"> <pre>irb (main):104:0> self => main irb (main):105:0> self.<span style="color:#0000ff;">class</span> => Object</pre> </div> <p> 一启动 <code>irb</code>,Ruby 解释器就为您创建主对象。这一主对象在 Ruby 相关的文章中也被称为<em>顶层上下文</em>。</p> <p> 关于 <code>self</code> 就介绍这么多了。下面我们接着来看动态方法和 <code>method_missing</code> 方法。</p> <p> <a name="method_missing"></a>method_missing 揭秘</p> <p> 看一下清单 14 中的 Ruby 代码。</p> <p> <a name="list14"></a><strong>清单 14. 运行中的 method_missing</strong></p> <div class="cnblogs_code"> <pre>irb (main):135:0> <span style="color:#0000ff;">class</span> Test irb (main):136:1> <span style="color:#0000ff;">def</span> method_missing (method, *args) irb (main):137:2> puts <span style="color:#800000;">"</span><span style="color:#800000;">Method: #{method} Args: (#{args.join (', ')})</span><span style="color:#800000;">"</span> irb (main):138:2> end irb (main):139:1> end => nil irb (main):140:0> t = Test.new => <span style="color:#008000;">#</span><span style="color:#008000;"><Test:0x2c7b850></span><span style="color:#008000;"> </span>irb (main):141:0> t.f (23) Method: f Args: (23) => nil</pre> </div> <p> 显然,如果 voodoo 是您喜欢的,那么清单 14 会给您这个恩典。这里发生什么了呢?我们创建了一个 <code>Test</code> 类型的对象,然后调用了 <code>t.f</code>,以 <code>23</code> 作为参数。但是 <code>Test</code> 没有以 <code>f</code> 作为方法,您应当会得到一个 <code>NoMethodError</code> 或类似的错误消息。Ruby 在这里做了一件很棒的事情:您的方法调用被阻截并由 <code>method_missing</code> 处理。<code>method_missing</code> 的第一个参数是缺失的方法名,在本例中是 <code>f</code>。第二个(也是最后一个)参数是 <code>*args</code>,该参数捕获传递给 <code>f</code> 的参数。您可以在何处使用像这样的参数呢?在众多选项之中,您可以轻松地将方法调用转发到一个包含的 <code>Module</code> 或一个组件对象,而不为顶级类中的每个调用显式提供一个包装应用程序编程接口。</p> <p> 在清单 15 中查看更多 voodoo。</p> <p> <a name="list15"></a><strong>清单 15. 使用 send 方法将参数传递给一个例程</strong></p> <div class="cnblogs_code"> <pre>irb (main):142:0> <span style="color:#0000ff;">class</span> Test irb (main):143:1> <span style="color:#0000ff;">def</span> method1(s, y) irb (main):144:2> puts <span style="color:#800000;">"</span><span style="color:#800000;">S: #{s} Y: #{y}</span><span style="color:#800000;">"</span> irb (main):145:2> end irb (main):146:1> end => nil irb (main):147:0>t = Test.new irb (main):148:0> t.send (:method1, 23, 12) S: 23 Y: 12 => nil</pre> </div> <p> 在清单 15<code>class Test</code> 有一个名为 <code>method1</code> 的方法被定义。但是,这里没有直接调用方法,而是发出对 <code>send</code> 方法的调用。<code>send</code> 是 <code>Object</code> 类的一个公共方法,因此可用于 <code>Test</code>(记住,所有类都派生自 <code>Object</code>)。<code>send</code> 方法的第一个参数是表示方法名称的一个符号和字符串。<code>send</code> 方法可以做到哪些您通常无法做到的事情?您可以使用 <code>send</code> 方法访问一个类的私有方法。当然,对于这是否是一个好特性仍然颇具争议。看一下清单 16 中的代码。</p> <p> <strong>清单 16. 访问类私有方法</strong></p> <div class="cnblogs_code"> <pre>irb (main):258:0> <span style="color:#0000ff;">class</span> SendTest irb (main):259:1> private irb (main):260:1> <span style="color:#0000ff;">def</span> hello irb (main):261:2> puts <span style="color:#800000;">"</span><span style="color:#800000;">Saying Hello privately</span><span style="color:#800000;">"</span> irb (main):262:2> end irb (main):263:1> end => nil irb (main):264:0> y = SendTest.new => <span style="color:#008000;">#</span><span style="color:#008000;">< SendTest:0x2cc52c0></span><span style="color:#008000;"> </span>irb (main):265:0> y.hello NoMethodError: private method `hello<span style="color:#800000;">'</span><span style="color:#800000;"> called for #< SendTest:0x2cc52c0></span><span style="color:#800000;"> </span> <span style="color:#0000ff;">from</span> (irb):265 irb (main):266:0> y.send (:hello) Saying Hello privately => nil</pre> </div> <p> <a name="throw"></a>Throw 和 catch 并非表面那样</p> <p> 如果您像我一样具有 <code>C++</code> 工作背景,且试图编写异常安全代码,那么在看到 Ruby 有 <code>throw</code> 和 <code>catch</code> 关键词时会开始感到异常亲切。遗憾的是,<code>throw</code> 和 <code>catch</code> 在 Ruby 中的含义完全不同。</p> <p> Ruby 通常使用 <code>begin…rescue</code> 块处理异常。清单 17 提供了一个示例。</p> <p> <a name="list17"></a><strong>清单 17. Ruby 中的异常处理</strong></p> <div class="cnblogs_code"> <pre>begin f = File.open (<span style="color:#800000;">"</span><span style="color:#800000;">ruby.txt</span><span style="color:#800000;">"</span>) <span style="color:#008000;">#</span><span style="color:#008000;"> .. continue file processing </span><span style="color:#008000;"> </span>rescue ex => Exception <span style="color:#008000;">#</span><span style="color:#008000;"> .. handle errors, if any </span><span style="color:#008000;"> </span>ensure f.close unless f.nil? <span style="color:#008000;">#</span><span style="color:#008000;"> always execute the code in ensure block </span><span style="color:#008000;"> </span>end</pre> </div> <p> 在清单 17 中,如果在试图打开文件时出错(可能是缺少文件或文件权限方面的问题),<code>rescue</code> 块中的代码会运行。<code>ensure</code> 块中的代码始终运行,不管是否有任何异常引发。注意,<code>rescue</code> 块后面是否紧跟 <code>ensure</code> 块是可选的。另外,如果必须显式地抛出一个异常,那么语法是 <code>raise <MyException></code>。如果您选择拥有您自己的异常类,可能会希望从 Ruby 内置的 <code>Exception</code> 类派生出相同的类,以利用现有方法。</p> <p> Ruby 中的 catch 和 throw 代码块实际上不是异常处理:您可以使用 <code>throw</code> 修改程序流程。清单 18 显示了一个使用 <code>throw</code> 和 <code>catch</code> 的示例。</p> <p> <a name="list18"></a><strong>清单 18. Ruby 中的 Throw 和 catch</strong></p> <div class="cnblogs_code"> <pre>irb (main):185:0> catch :label do irb (main):186:1* puts <span style="color:#800000;">"</span><span style="color:#800000;">This will print</span><span style="color:#800000;">"</span> irb (main):187:1> throw :label irb (main):188:1> puts <span style="color:#800000;">"</span><span style="color:#800000;">This will not print</span><span style="color:#800000;">"</span> irb (main):189:1> end This will <span style="color:#0000ff;">print</span> => nil</pre> </div> <p> 在清单 18 中,当代码运行到 <code>throw</code> 语句时,执行会被中断,解释器开始寻找处理相应符号的一个 <code>catch</code> 块。在 <code>catch</code> 块结束的地方继续执行。查看清单 19 中的 <code>throw</code> 和 <code>catch</code> 示例:注意,您可以轻松将 <code>catch</code> 和 <code>throw</code> 语句用于各个函数。</p> <p> <a name="list19"></a><strong>清单 19. Ruby 中的异常处理:嵌套的 catch 块</strong></p> <div class="cnblogs_code"> <pre>irb (main):190:0> catch :label do irb (main):191:1* catch :label1 do irb (main):192:2* puts <span style="color:#800000;">"</span><span style="color:#800000;">This will print</span><span style="color:#800000;">"</span> irb (main):193:2> throw :label irb (main):194:2> puts <span style="color:#800000;">"</span><span style="color:#800000;">This won't print</span><span style="color:#800000;">"</span> irb (main):195:2> end irb (main):196:1> puts <span style="color:#800000;">"</span><span style="color:#800000;">Neither will this print</span><span style="color:#800000;">"</span> irb (main):197:1> end This will <span style="color:#0000ff;">print</span> => nil</pre> </div> <p> 有些人甚至说,Ruby 中对 <code>catch</code> 和 <code>throw</code> 的支持将 <code>C goto</code> 行为带到一个全新的高度。鉴于函数可以有多个嵌套层,而 <code>catch</code> 块可能在每一级,<code>goto</code> 行为类比似乎有据可循。</p> <p> <a name="threads"></a>Ruby 中的线程可以是绿色的</p> <p> Ruby 版本 1.8.7 不支持真正的并发性。确实不支持。但是您会说,在 Ruby 中有 Thread 构造函数。您说的没错。不过这个 <code>Thread.new</code> 不会在您每次调用同一方法时生成一个真实的操作系统线程。Ruby 支持的是<em>绿色线程</em>:Ruby 解释器使用单一操作系统线程来处理来自多个应用程序级线程的工作负载。</p> <p> 当某个线程等待一些输入/输出发生时,这一 “绿色线程” 概念很有用,而且您可以轻松调度一个不同的 Ruby 线程来充分利用 CPU。但是这一构造函数无法使用现代的多核 CPU(维基百科提供了一段内容,很好地解释了什么是绿色线程。参见 <a href="/misc/goto?guid=4958344104868813514">参考资料</a> 获取链接)。</p> <p> 最后这一个示例(参见清单 20)证明了这一点。</p> <p> <strong>清单 20. Ruby 中的多个线程</strong></p> <div class="cnblogs_code"> <pre><span style="color:#008000;">#</span><span style="color:#008000;">!/usr/bin/env ruby</span><span style="color:#008000;"> </span> <span style="color:#0000ff;">def</span> func (id, count) i = 0; <span style="color:#0000ff;">while</span> (i < count) puts <span style="color:#800000;">"</span><span style="color:#800000;">Thread #{i} Time: #{Time.now}</span><span style="color:#800000;">"</span> sleep (1) i = i + 1 end end puts <span style="color:#800000;">"</span><span style="color:#800000;">Started at #{Time.now}</span><span style="color:#800000;">"</span> thread1 = Thread.new{func (1, 100)} thread2 = Thread.new{func (2, 100)} thread3 = Thread.new{func (3, 100)} thread4 = Thread.new{func (4, 100)} thread1.join thread2.join thread3.join thread4.join puts <span style="color:#800000;">"</span><span style="color:#800000;">Ending at #{Time.now}</span><span style="color:#800000;">"</span></pre> </div> <p> 假设您的 Linux® 或 UNIX® 机器上拥有 <code>top</code> 实用程序,在终端运行代码,获取进程 ID,然后再运行 <code>top –p <process id></code>。<code>top</code> 启动后,按住 Shift-H 来列出运行中线程的数量。您应当只能看到一个线程,确认了这一点:Ruby 1.8.7 中的并发性不过是个神话。</p> <p> 总的看来,绿色线程没有什么坏处。它们在重负荷输入/输出密集型程序中仍然有用,更不用说该方法可能是操作系统间最可移植的一个了。</p> <p> 结束语</p> <p> 本文涵盖了以下多个方面:</p> <ul> <li>Ruby 中类层次结构的概念</li> <li>单例方法</li> <li>解释 <code>self</code> 关键词和 <code>method_missing</code> 方法</li> <li>异常</li> <li>线程</li> </ul> <p> 尽管 Ruby 不乏特立独行之处,但是使用它进行编程还是挺有趣的,而且其以最少的代码完成大量工作的能力还是很强大的。难怪 推ter 这样的大型应用程序会使用 Ruby 来驾驭其真正的潜力。祝您有个快乐的 Ruby 编程体验!</p> <p> 参考资料</p> <p> <strong>学习</strong></p> <ul> <li>阅读 <a href="/misc/goto?guid=4958344105676459885">Programming Ruby: The Pragmatic Programmers' Guide</a>(Dave Thomas,Chad Fowler 和 Andy Hunt;第二版),这是一本 Ruby 必读书籍,也就是广为人知的 Pickaxe 图书。</li> <li>查阅另一个宝贵的 Ruby 资源 <a href="/misc/goto?guid=4958344106456808409">The Ruby Programming Language</a> [Yukihiro "Matz" Matsumoto(Ruby 的创建者)和 David Flanagan,O'Reilly,2008 年]。</li> <li>访问 <a href="/misc/goto?guid=4958344107254059858">To Ruby From C and C++</a>,这是一个面向希望学习 Ruby 的 <code>C/C++</code> 程序员的一个不错站点。</li> <li>在维基百科上了解更多有关 <a href="/misc/goto?guid=4958344108042224343">绿色线程</a> 的解释信息。</li> <li><a href="/misc/goto?guid=4958344108837197807">IBM Rational 推ter</a>。</li> <li>观看 <a href="/misc/goto?guid=4958344109632322517">演示如何用 WebSphere Studio 快速开发 Web Services</a>,包括面向初学者的产品安装和设置演示,以及为经验丰富的开发人员提供的高级功能。</li> <li>在 <a href="/misc/goto?guid=4958344110408171099">developerWorks Linux 专区</a> 寻找为 Linux 开发人员(包括 <a href="/misc/goto?guid=4958344111201719539">Linux 新手入门</a>)准备的更多参考资料,查阅我们 <a href="/misc/goto?guid=4958344111988835699">最受欢迎的文章和教程</a>。</li> <li>在 developerWorks 上查阅所有 <a href="/misc/goto?guid=4958344112795764768">Linux 技巧</a> 和 <a href="/misc/goto?guid=4958344113588819161">Linux 教程</a>。</li> <li>随时关注 developerWorks <a href="/misc/goto?guid=4958344114384464322">技术活动</a>和<a href="/misc/goto?guid=4958344115196035019">网络广播</a>。</li> <li>访问 developerWorks <a href="/misc/goto?guid=4958344115989055723">Open source 专区</a>获得丰富的 how-to 信息、工具和项目更新以及<a href="/misc/goto?guid=4958344116783737104">最受欢迎的文章和教程</a>,帮助您用开放源码技术进行开发,并将它们与 IBM 产品结合使用。</li> </ul> <p> <strong>获得产品和技术</strong></p> <ul> <li>使用 <a href="/misc/goto?guid=4958344117583345151">IBM 产品评估试用版软件</a>(可以通过下载获得),并使用专门面向开发人员的软件改进您的下一个开源开发项目。</li> </ul> <p> <strong>讨论</strong></p> <ul> <li>加入 <a href="/misc/goto?guid=4958344118369454520">developerWorks 中文社区</a>,developerWorks 社区是一个面向全球 IT 专业人员,可以提供博客、书签、wiki、群组、联系、共享和协作等社区功能的专业社交网络社区。</li> <li>加入 <a href="/misc/goto?guid=4958344119172756503">IBM 软件下载与技术交流群组</a>,参与在线交流。</li> </ul> <p><strong> 关于作者</strong></p> <p> Arpan Sen 是致力于电子设计自动化行业的软件开发首席工程师。他使用各种 UNIX 版本(包括 Solaris、SunOS、HP-UX 和 IRIX)以及 Linux 和 Microsoft Windows 已经多年。他热衷于各种软件性能优化技术、图论和并行计算。Arpan 获得了软件系统硕士学位。<br /> <br /> 来自: <a id="link_source2" href="/misc/goto?guid=4958344119965045133" target="_blank">www.ibm.com</a></p>