首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Ruby MetaProgramming中的一个令人困惑的案例

Ruby MetaProgramming中的一个令人困惑的案例
EN

Stack Overflow用户
提问于 2012-12-08 08:25:50
回答 3查看 269关注 0票数 4

我是红宝石MetaProgramming的初学者。在irb中练习我的代码时,我遇到了这个问题。

代码语言:javascript
复制
class A; end
a = A.new
b = class << a; self; end

b.instance_eval { def foo; puts 'foo'; end }
# => works for b.foo

b.instance_eval { define_method :bar do; puts 'bar'; end }
# => WHY this one works for a.bar rather than b.bar

最后一个代码片段让我感到困惑。

谢谢你的具体回答,但也许我没有清楚地解释我的困惑。我真正想要理解的是,为什么define_method在这些情况下的行为如此不同,在这里:

代码语言:javascript
复制
class A
  def foo1
    p 'foo1 from A'
  end 

  define_method :bar1 do
    p 'bar1 from A'
  end 
end

a = A.new
a.foo1 # => 'foo1 from A'
a.bar1 # => 'bar1 from A'


a.instance_eval { def foo2; p 'foo2 from a.metaclass'; end }
a.foo2 # => 'foo2 from a.metaclass'
a.instance_eval { define_method :bar2 do; p 'bar2 from a.metaclass'; end }
# => NoMethodError: undefined method `define_method' for #<A:0x000000016a2e70>

aa = class << a; self; end
aa.instance_eval { def foo3; p 'foo3 from a.metaclass.metaclass'; end }
aa.foo3 # => 'foo3 from a.metaclass.metaclass'
aa.instance_eval { define_method :bar3 do; p 'bar3 from a.metaclass.metaclss'; end }
aa.bar3 # => NoMethodError: undefined method `bar3' for #<Class:#<A:0x000000016a2e70>>
a.bar3 # => 'bar3 from a.metaclass.metaclss'

我知道这不是在日常编码中出现的,但我想让我的头脑清楚。

得出结论:

代码语言:javascript
复制
aa = class << a; self; end 

aa.instance_eval { def foo; puts 'foo..'; end }

# defines a singleton-method for aa
aa.foo # => 'foo...'


aa.instance_eval { define_method :bar do; puts 'bar..'; end }
# equals
aa.class_eval { def bar; puts 'bar..'; end }

# both define a singleton-method for a,
# as define_method and class_eval both define instance_method
a.bar # => 'bar...'
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-12-09 19:36:55

除所有其他评论外:

方法Object#instance_eval允许您将self设置为任意对象,使用self计算块中的代码,然后重置self。

Module#define_method :在接收方self中定义一个实例方法,该方法必须是(匿名)类或模块。

代码语言:javascript
复制
singleton_class_of_object_a = aa = class << a; self; end
aa.instance_eval { def foo3; puts "foo3 from singleton class of a, self=#{self}"; end }
aa.foo3 # => foo3 from singleton class of a, self=#<Class:#<A:0x007fc2e4049e68>>
aa.instance_eval do
    puts "about to define_method :bar3 in self=#{self}"
    define_method :bar3 do; puts "bar3 from singleton class of a, self=#{self}"; end
end # => about to define_method :bar3 in self=#<Class:#<A:0x007fc2e4049e68>>
a.bar3 # => bar3 from singleton class of a, self=#<A:0x007fc2e4049e68>

define_method :bar3是在singleton_class_of_object_a (匿名类,见下文)的上下文中执行的,因此定义了该类的实例方法,因此bar3成为a的单例方法。正如我在前面的回答中已经说过的,它等同于直接定义对象:

代码语言:javascript
复制
def a.bar4; puts 'bar4 from singleton class of a' end
a.bar4 # => bar4 from singleton class of a

p a.singleton_methods.sort # => [:bar3, :bar4, :foo2]
p a.methods(false).sort # => [:bar3, :bar4, :foo2]  

a = A.new之后,实例a的字段类指向A类。

使用class << adef a.bar4,Ruby创建一个匿名类,实例a的字段类现在指向这个匿名类,并从那里指向A

在此上下文中使用defdefine_method定义的方法进入匿名类的方法表。

票数 2
EN

Stack Overflow用户

发布于 2012-12-08 08:45:31

因为在您的例子中,def foo充当def self.foo

define_method :bar扮演def bar的角色。

这是因为instance_eval 创建类方法

您的代码与以下代码相同:

代码语言:javascript
复制
class << a
  def self.foo
    puts 'foo'
  end

  def bar
    puts 'bar'
  end
end

因此,foo方法定义在a的特征类中。

bar方法在a本身中定义。

如果您需要a.foo来工作,则使用class_eval代替。

它将工作,因为class_eval 创建实例方法

代码语言:javascript
复制
b.class_eval { def foo; puts 'foo'; end }
a.foo
# => foo
票数 1
EN

Stack Overflow用户

发布于 2012-12-08 15:09:23

代码语言:javascript
复制
b.instance_eval { def foo; puts 'foo'; end }
b.instance_eval { puts "self in b.instance_eval block=#{self}" }
#=> self in b.instance_eval block=#<Class:#<A:0x007fe3c204d000>>
b.foo #=> foo

您正在A的单个实例的单例类中定义一个方法,它看起来非常复杂。相反,直接在实例上定义一个单例方法:

代码语言:javascript
复制
cat = String.new("cat")
def cat.speak
    'miaow'
end
cat.speak #=> "miaow" 
cat.singleton_methods #=> ["speak"] 

class <<表示法,如

代码语言:javascript
复制
singleton_class_of_A = eigenclass_of_A = class << A; self; end

通常用于定义“类方法”(实际上是A的单例方法),或类的实例变量:

代码语言:javascript
复制
class B
    class << self
        def my_class_method_of_B
            puts "my_class_method_of_B"
        end
        @my_first_variable_of_class_B = 123
        attr_accessor :my_second_variable_of_class_B
    end
end

B.my_class_method_of_B
print 'singleton methods of B : '
p B.singleton_methods
print 'instance variables of B : '
p B.instance_variables
print 'class variables of B : '
p B.class_variables
print '"singleton variables" of B : '
class << B; p instance_variables end

产出:

代码语言:javascript
复制
my_class_method_of_B
singleton methods of B : [:my_class_method_of_B, :my_second_variable_of_class_B, :my_second_variable_of_class_B=]
instance variables of B : []
class variables of B : []
"singleton variables" of B : [:@my_first_variable_of_class_B]

如你所见,这件事并不容易,你不妨看看http://pragprog.com/book/ppmetr/metaprogramming-ruby

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13775794

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档