提这个问题之前,我去 https://gitter.im/rails/rails 也发表过,不过没有得到解决方案。
直接上代码,现在在类A
中定义了跨作用域的类B
,我希望可以在B
中访问到A
的实例方法hello
.
尝试了很久,没有实现 - -求大神指导一下。
class A
attr_accessor :name
def hello
"Hello #{name}"
end
B = Class.new do
define_singleton_method :hi do
hello
end
end
end
class C < A
def say_hi
B.hi
end
end
obj = C.new(:name => 'John')
obj.say_hi #=> 希望得到 Hello John
问题是B.new
只是创建了一个name
为nil
的class
,所以并不继承自A
,父类是Object
,所以你这样写B.hello
是还没有定义的,如果定义def hello
那么可以用A::B.new.hello
来调用 但因为B
并不继承A
,所以A::B:hello
方法也不能super
我看了下p self.methods
和p A::B.methods
没找到怎么声明继承 不会这种情况声明继承
class A
attr_accessor :name
def hello
puts "Hello #{name}"
end
B = Class.new do
p self.methods
p self.name #=> nil
p self.class #=> class
define_singleton_method :hi do
def hello
p 'hello1'
#super
end
end
end
p B.class #=> class
end
class C < A
def say_hi
B.hi
end
end
p A::B.superclass #=> Object
obj = C.new
obj.name='John'
obj.say_hi
a=A::B.new
p a.methods
a.hello #=> "hello1"
p A::B.methods
恩 分析的有道理,但是还是有几点疑问。 1 我也没有说过 B 继承 A 呀😂 2 你的 B.new.name 为空,我猜测这个 name 不是 A 的 name,而是 class 的 name,因为这个时候 block 还没执行完,所以为 nil 3 如果你把 hello 改成 self.hello 再试试😊
B 是一个 class 又不继承 A 那 B 里面调用的 hello 是从哪里来的。name 的值不应该是到命令行查看才知道的么,为什么还要猜测。self.hello 是一个 class.hello 所以它当然调用 class 内方法 而这个 class 里并没有声明 hello 方法 它又不继承自 A 无法 super 他只是 A 中一个 class.new 实例并不是 A.new,它凭什么可以调用 A 的 hello 方法呢
不能这么说,因为无法没问题,我也知道这代码执行有问题,所以希望又说来分析下原因究竟出在哪里。至于代码出于什么目的,只能说没有目的,凭空想的。
事实是 B 不能调用 hello,这也是我提问的原因,我想知道为什么,如果 hello 是一个变量,比如字符串,这个时候 B 却又可以调用 hello,这就是让人很困惑的事。
你的问题可以简化为:
class A
def a_method
"call from a"
end
end
class B
def b_method
a_method
end
end
B 定义在 A 里面和外面对于实例方法并没有什么不同——实例方法需要通过实例调用。
想要用 B 调用 A 的方法,那么 new 一个实例:
class B
def b_method
A.new.a_method
end
end
class A # 新的作用域
attr_accessor :name
def hello # 新的作用域
"Hello #{name}"
end
B = Class.new do # 没有进入新的作用域
define_singleton_method :hi do
hello # self是B,但是B中没有定义hello
end
end
end
其实你这个问题在《ruby 元编程》里有答案
实际上,你搞混了一个东西。当 hello
是 字符串与 def hello; end
是完全两个不同的东西。
我认为你指的 hello
是这个意思。
class A
hello = "test"
def hello_again
end
B = Class.new do
define_singleton_method :hi do
hello
end
end
end
class C < A
def say_hi
B.hi
end
end
hello
可以被访问到。因为解释器在读取这段代码时,当读到define_singleton_method
,此时do
block 是可以访问当前的作用域的。
那么当前的作用域到底是什么?并不是class A
内部的作用域,而是,正在定义class A
的作用域,也可以理解为 Class.new do ; end
这个作用域。
本来,如果你没有在 B.hi
中使用 hello
的话,这个hello
在加载完这段代码后 hello
这个变量就释放了(有可能有理解的错误),因为除了在定义 A 的代码里用了一下,其他地方就再也没有绑定过这个变量了,但是你定义了 B.hi
,把这个变量又和 B.hi
绑定到一起了。所以 hello
这个变量没有被释放掉。
上面解释了为什么字符串hello
可以被访问。
上面的这些东西在The Ruby Programming Language
这本书里有讲到过。可以看一下。
之前没看你后面的评论。发现你说 hello 是变量的问题,你说“很奇怪”。其实一点也不奇怪,只因为你没搞懂作用域
、当前类
、self
、闭包
这些概念。
你用 Class.new 在 A 中创建一个类,这时并没有打开新的作用域,所以那个hi
方法可以访问相同上下文中的hello 变量。而hello 方法的调用,要通过一个叫方法查找
的过程,这个过程重度依赖self
,你在 B 类中直接调用hello
方法,默认self
就是 B Class,而 B 中没有hello
方法,所以你在hi
中调用hello
会报NoMethodError
。
还有就是 A 中的hello
方法是个实例方法,实例方法要通过一个类的实例来调用,直接用没有接收者的方式调用是不行的,语法错误。
劝你多读书
==================================
再补充一点,你还要研究一下 ruby 的对象模型,看看类、对象、实例变量、实例方法这些内容的关系,你会发现实例变量跟实例方法保存在不同的地方,然后你的问题就豁然开朗了
谢谢你的耐心解答。其实我之所以提这个问题就是因为肯定没有完全清楚的地方嘛,当然其中也不乏各种测试,得到的结果没有说得过去的理论支持。所以跑过去求大神解惑。您刚说def hello
和str hello
是不一样的,我觉得可能还有种特殊情况说不过去,麻烦看一下,在 irb 中,不在 A 类中的情况:
$ irb
irb(main):001:0> def hello
irb(main):002:1> "Hello world"
irb(main):003:1> end
=> :hello
irb(main):004:0> B = Class.new do
irb(main):005:1* define_singleton_method :hi do
irb(main):006:2* hello
irb(main):007:2> end
irb(main):008:1> end
=> B
irb(main):009:0> B.hi
=> "Hello world"
说得过去的,思路和我上面说的一样,你思考一个问题,在 irb 中,当前的作用域是什么?define_singleton_methon 时,这个 do block 可以访问的作用域是什么?我能告诉你的是,和上面的情况是一样的,他们在同一个作用域。基本上@laplace已经指出了问题了。文字比较生硬,可以忽略大家的语气。不用在意。