class Numeric
@@currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
def method_missing(method_id, *args, &block)
singular_currency = method_id.to_s.gsub(/s$/, '')
if @@currencies.has_key?(singular_currency)
self * @@currencies[singular_currency]
else
super
end
end
end
puts 3.yen
# Output is
# 0.039
我的问题是,如果我们使用即时变量@currencies替换@@ currency并添加了attr_reader,那么为什么这段代码不会起作用?货币
像这样的东西
class Numeric
@currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
attr_accessor :currencies
def method_missing(method_id, *args, &block)
singular_currency = method_id.to_s.gsub(/s$/, '')
if @currencies.has_key?(singular_currency)
self * @currencies[singular_currency]
else
super
end
end
end
puts 3.yen
# Output
# method_missing': undefined method `has_key?' for nil:NilClass (NoMethodError)
# from Untitled.rb:15:in `<main>'
Isn&#t; t 3已经是类Numeric的瞬间,因此,setter货币应该能够工作并返回正确的散列组合?
编辑:那么method_missing是一个静态方法呢?为什么不用self.method_missing定义?
答案 0 :(得分:1)
在类声明的上下文中设置@currencies
会在类本身上设置一个实例变量:
Numeric.instance_variable_get(:@currencies)
#=> {"yen"=>0.013, "euro"=>1.292, "rupee"=>0.019}
另一方面,@currencies
类和method_missing
访问者中的currencies
是指特定数字实例上的@currencies
变量,其中不是{\ n}}定义:
Numeric.new.instance_variable_get(:@currencies)
#=> nil
您可以通过在类本身上定义访问器并使用method_missing
方法调用该访问器来解决此问题:
class Numeric
@currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
class << self
attr_accessor :currencies
end
def method_missing(method_id, *args, &block)
singular_currency = method_id.to_s.gsub(/s$/, '')
if self.class.currencies.has_key?(singular_currency)
self * self.class.currencies[singular_currency]
else
super
end
end
end
Numeric.currencies
#=> {"yen"=>0.013, "euro"=>1.292, "rupee"=>0.019}
但这种方法仍然存在问题。即使currencies
访问器现在引用类上的实例变量(而不是之前该类的特定实例上的实例变量),@currencies
仍然只在{ {1}}类,而不是它的任何子类:
Numeric
要解决此问题,您可以修改属性访问者以自动为每个单独的类提供默认值(因此Fixnum.currencies
#=> nil
和Fixnum
每个都有自己独立的Float
变量,或者回到使用类变量,如下所示:
@currencies
答案 1 :(得分:0)
实例变量仅适用于类的实例。类变量可供整个班级使用。
换句话说,代码中的@currencies.has_key?
未定义,因为它无法在第2行看到@currencies
。
答案 2 :(得分:0)
问题是您在方法之外初始化@currencies
。任何不在方法内的代码都在类对象的上下文中进行评估,而不是类的实例。
看看这个示例类:
class Foo
@bar = "baz"
def self.bar; @bar; end
def bar; @bar; end
end
现在让我们看看我们定义的这两种方法的结果。
Foo.bar # => "baz"
Foo.new.bar # => nil
这意味着@bar
属于Foo
类,而不属于Foo
的实例。
您的问题可以通过在方法中初始化@currency
来解决,通常是initialize
:
class Numeric
def initialize
@currency = ...
end
end
答案 3 :(得分:0)
你可以这样做:
class Numeric
@currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
class << self
attr_reader :currencies
end
def method_missing(method_id, *args, &block)
singular_currency = method_id.to_s.gsub(/s$/, '')
if Numeric.currencies.key?(singular_currency)
self * Numeric.currencies[singular_currency]
else
super
end
end
end
行:
class << self
attr_reader :currencies
end
为类实例变量@currencies
创建一个读访问器。
你可能倾向于写(就像我最初做的那样):
if self.class.currencies.key?(singular_currency)
但这不起作用,因为method_missing
是3
的{{1}},Fixnum
的实例,Numeric
的子类。回想一下,子类不能直接访问他们的祖先类方法。这就是为什么我们需要明确地将Numeric
识别为接收者。
如:
Fixnum.ancestors => [Fixnum, Integer, Numeric,...]
我们看到了:
if self.class.superclass.superclass.currencies.key?(singular_currency)
适用于Fixnum
,但不适用于Numeric
的其他子类:Bignum
和Float
。