Ruby中自定义方法无法作用于File内置类的问题
Ruby中自定义方法无法作用于File内置类的问题
我来帮你捋一捋这个奇怪的问题~ 你给Object和Array都加了自定义的foo方法,处理普通的整数、字符串数组时完全正常,但换成File对象数组后,就只有Array#foo的输出,每个File实例的foo都没执行,这确实挺让人困惑的。
先还原一下你的代码和现象:
首先是方法定义:
class Object def foo puts("Object#foo on #{self.inspect}") end end class Array def foo puts("Array#foo on #{self.inspect}") self.each do |e| e.foo end end end
当处理整数数组时:
a = [1,2] a.foo
输出符合预期:
Array#foo on [1, 2]
Object#foo on 1
Object#foo on 2
但换成File对象数组后:
b = [File.new('f1','w'),File.new('f2','w')] b.foo
却只输出:
Array#foo on [#<File:f1>, #<File:f2>]
问题原因
其实根源在Ruby的IO类(File是它的子类)的特殊方法查找逻辑上:
虽然Object是所有Ruby类的父类,但IO/File这类核心对象是用C语言实现的,它们重写了method_missing方法。当你调用一个IO实例上不存在的方法时,它会尝试把方法名转换成底层的IO控制命令(比如ioctl调用),而不是直接去父类Object找方法。
当你调用File#foo时,Ruby会尝试把foo当成一个IO控制码,这显然是无效的,所以会抛出一个底层异常(比如Errno::EINVAL: Invalid argument)。如果你的运行环境没有显示stderr输出,你就看不到异常信息,看起来就像是e.foo根本没执行一样——实际上是第一次调用就抛出异常,导致each循环直接中断了。
解决方案
有几种简单的解决方式:
- 给IO/File类显式定义
foo方法
直接给IO类(覆盖所有子类包括File)添加foo方法,这样就不会触发它的特殊method_missing逻辑:
class IO def foo # 直接复用Object的实现 super end end
或者直接写具体实现:
class IO def foo puts("Object#foo on #{self.inspect}") end end
- 在遍历中捕获异常(用于排查验证)
如果只是想确认问题,可以在Array#foo的循环里加异常捕获,看看是不是真的抛出了错误:
class Array def foo puts("Array#foo on #{self.inspect}") self.each do |e| begin e.foo rescue => err puts "调用foo时出错(对象:#{e.inspect}):#{err}" end end end end
运行后就能看到具体的异常信息,验证我们的猜测。
备注:内容来源于stack exchange,提问作者jean marc
相关产品推荐
相关产品推荐

