o = Object.new
o.instance_eval { @str = "foo" }
p o # => #<Object:0x5dd1a0 @foo="bar">
Run Code Online (Sandbox Code Playgroud)
这很好.p使用对象作为参数调用将打印对象inspect方法的输出.但是,不幸的是,如果对象有一个to_s方法被覆盖,那么它将输出该输出:
class << o
def to_s; @str; end
end
p o.to_s # => "foo"
p o # => foo
Run Code Online (Sandbox Code Playgroud)
所以要解决这个问题,我们必须inspect在对象上定义一个方法:
class << o
def inspect; "blah"; end
end
p o # => "blah"
Run Code Online (Sandbox Code Playgroud)
如何使我的对象的inspect方法输出默认的Ruby方式,如我的第一个代码示例的第3行所示?
我最接近的是下面的,但我不确定它是否正确
class << o
def inspect
vars = instance_variables.collect { |v| v.to_s << "=#{instance_variable_get(v).inspect}"}.join(", ")
"#<#{self.class}:0x#{object_id} #{vars}>"
end
end
Run Code Online (Sandbox Code Playgroud)
小智 10
要使数字与原始实现匹配,您只需要将object_id向左移动一位,如下所示:
(object_id << 1).to_s(16)
Run Code Online (Sandbox Code Playgroud)
必须有一个额外的位用于标志.
默认inspect方法结果非常复杂,因为它需要正确处理对自身的递归调用.这是一个基于Rubinius源代码的实现,它忽略了存在to_s.
module DefaultInspect
Thread.current[:inspected_objects] = {}
def inspected_objects
Thread.current[:inspected_objects]
end
def inspect_recursion_guard
inspected_objects[object_id] = true
begin
yield
ensure
inspected_objects.delete object_id
end
end
def inspect_recursion?
inspected_objects[object_id]
end
def inspect
prefix = "#<#{self.class}:0x#{self.__id__.to_s(16)}"
# If it's already been inspected, return the ...
return "#{prefix} ...>" if inspect_recursion?
# Otherwise, gather the ivars and show them.
parts = []
inspect_recursion_guard do
instance_variables.each do |var|
parts << "#{var}=#{instance_variable_get(var).inspect}"
end
end
if parts.empty?
str = "#{prefix}>"
else
str = "#{prefix} #{parts.join(' ')}>"
end
str.taint if tainted?
return str
end
end
Run Code Online (Sandbox Code Playgroud)
要使用此模块,您可以执行以下操作:
class Foo
include DefaultInspect
def to_s
@foo
end
end
f = Foo.new
f.instance_eval { @foo = f }
p f #=> #<Foo:0x8042ad58 @foo=#<Foo:0x8042ad58 ...>>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1688 次 |
| 最近记录: |