想象一下,我想编写自己的数学运算符,如“+”
简单版本将是:
def plus(a,b)
return a+b
end
但这不是真正的“+”所做的。
我想要3.add(4) # =>7
但是,如何告诉ruby采用我使用我的方法的对象?
我试过
def add(c)
return self+c
end
但我收到错误消息:
:在<main>': private method
添加'调用3:Fixnum(NoMethodError)
答案 0 :(得分:1)
ruby中的加号(+)可以像任何其他方法一样被覆盖(你可以查找运算符重载):
class MyOperator
attr_accessor :text
def initialize(text)
@text = text
end
def +(operand)
"#{self.text} #{operand.text}"
end
def to_s
self.text
end
end
a = MyOperator.new "Hello"
b = MyOperator.new "World"
puts (a+b)
因此没有太大的魔力。但是如果操作符的重载在你的上下文中有意义,你必须要小心。
答案 1 :(得分:1)
问题
您定义了方法:
def add(c)
return self + c
end
并尝试使用它:
3.add(4) #=> NoMethodError: private method `add' called for 3:Fixnum
了解此错误消息
此错误消息确切地告诉您问题所在。我认为你的问题只是你不明白Ruby如何在对象上调用方法。
当Ruby看到3.add(4)
时,它首先查看接收器,3
,并确定:
3.class #=> Fixnum
这告诉它定义方法add
的位置:在类Fixnum
中或在Fixnum
的祖先的类或模块之一中。
所以它在那里查找,找不到它,并发出错误消息。我们可以确认它不存在:
Fixnum.instance_methods.include?(:add)
#=> false
那么add
定义在哪里?
你确实定义了它,所以它在哪里?我们来看看:
method(:add).owner
#=> Object
Object.instance_methods.include?(:add)
#=> false
Object.instance_methods
返回在Object
和Object
的祖先上定义的所有 public 实例方法的数组。 add
不在其中,因此我们得出结论add
是受保护的或私有方法:
Object.protected_instance_methods.include?(:add)
#=> false
Object.private_instance_methods.include?(:add)
#=> true
让我们尝试在Object
:
Object.new.add(4)
#=> NoMethodError:
# private method `add' called for #<Object:0x007fdb6a27fa68>
考虑到Object#add
是私有的,这是有道理的。但是,我们可以使用Object#send:
Object.new.send(:add,4)
#NoMethodError: undefined method `+' for #<Object:0x007fdb6a28e068>
作为练习,请确保您了解Ruby采取的导致她提出此异常的步骤(实例方法+
未在Object
上定义,或者等效地,Object
的实例{1}}没有方法+
)。
顺便问一下,你在哪里定义add
?那样,我指的是self
定义它时的价值是什么?我们来看看:
self #=> main
self.class #=> Object
我们看到必须在其接收者为其实例的类上定义add
。 (一口,是的,但这很重要,所以一定要明白这一点)。
为什么Object#add
是私有的而不是公开的?
考虑:
def greet
puts 'hi'
end
class A
end
A.private_instance_methods.include?(:add)
#=> true
A.new.send(:greet)
#=> 'hi'
这是因为A
从greet
继承Object
:
A.ancestors.include?(Object) #=> true
如果Object#greet
是公开的,那么您定义的每个内置类和每个类都将具有公共实例方法greet
。这会造成很大的痛苦。 (假设您有一个方法great
并将其输入错误greet
!)即使是私有greet
也可能会造成麻烦。)
应该在哪里定义add
从add.class => Fixnum
开始,我们就这样定义:
class Fixnum
def add(other)
self + other
end
end
Fixnum.instance_methods.include?(:add) #=> true
3.add(4) #=> 7
如果我在puts "self#{self}"
之后包含了class Fixnum
行,它就会打印出“Fixnum”。使用显示puts
值的self
语句对您的代码进行格式化通常有助于了解正在发生的事情。
最后一件事:
method(:add).owner
#=> NameError: undefined method `add' for class `Object'
为什么这不会返回Fixnum
?由于method
没有明确的接收者(即没有xx.method
),Ruby认为接收者是self
,这是:
self #=> main
所以她在method
中寻找方法self.class => Object
,你知道她发现了什么(或者,我应该说,找不到)。相反,我们需要写:
Fixnum.instance_method(:add).owner #=> Fixnum
或
3.method(:add).owner #=> Fixnum
此处3
当然可以替换为Fixnum
的任何实例。
注意我在某种程度上简化了这个解释。在搜索方法时,Ruby也会查看接收者的单例类。对于直接对象(数字,符号,true
,false
和nil
),这不是问题,因为它们没有单例类:
3.singleton_class #=> TypeError: can't define singleton
相比之下,例如:
[1,2].singleton_class #=> #<Class:#<Array:0x007fbcf18c01a8>>