类对象,单例类

时间:2011-12-25 19:43:12

标签: ruby oop metaprogramming

我在ruby中使用元编程,我有一个问题。我有一节课:

class Klass
  class << self
    @x = "yeah"
  end
end
b = Klass.new
a = class << Klass; self; end
a.instance_eval "@x"           #=> yeah
Klass.instance_eval "@x"       #=> nil

为什么呢?在变量a中,我有一个单例类,对吧?并且Klass.instance_eval执行单例类的上下文:

Klass.instance_eval "def yeah; puts 10; end"
Klass.yeah                     #=> 10

另外,解释器中的Klass指向类的上下文,是吗?并且a指向单例类的上下文? 这表示a.class_evala.instance_eval?我这样做:

a.instance_eval "def pops; puts 0; end"
a.class_eval "def popsx; puts 1; end"
a.pops                         #=> 0
a.popsx                        # FAIL
Klass.pops                     # FAIL
Klass.popsx                    #=> 1
b.pops; b.popsx                # DOUBLE FAIL

我不明白这一点。谢谢!

2 个答案:

答案 0 :(得分:3)

很难完全回答你的问题(关于Ruby的类模型的详细解释,请看Dave Thomas' excellent presentation),但是:

使用 class_eval ,您实际上定义了实例方法 - 就像您在类的主体内部一样。例如:

class Klass
end

Klass.class_eval do
  def instance_method
    puts 'instance method'
  end
end

obj = Klass.new
obj.instance_method  # instance method

使用 instance_eval ,您实际上定义了类方法 - 就好像您在给定对象的单例(eigenclass)类的主体内部一样(在Ruby中,类也是对象)。例如:

Klass.instance_eval do
  def class_method
    puts 'class method'
  end
end

Klass.class_method  # class method

在你的情况下:

Klass.instance_eval "@x"不起作用,因为@x不是Klass的一部分,它是Klass'单身类的一部分:

class Klass
  class << self
    @x = "yeah"
  end

  puts @x
end

# prints nothing

a.instance_eval "@x"工作正常,因为您在a单例类的上下文中评估“@x”,该类与您定义@x实例的Klass类的单例类相关联变量。这个例子可以说明单例类如何互连:

class Foo
end

f = class << Foo; self; end
g = class << Foo; self; end

f.instance_eval "def yeah; puts 'yeah'; end"

g.yeah  # yeah

g.instance_eval "def ghee; puts 'ghee'; end"

f.ghee  # ghee

Klass.instance_eval "def yeah; puts 10; end"定义了'普通'类方法。因此Klass.yeah工作正常(请参阅上一个示例中的Klass.class_method)。

a.instance_eval "def pops; puts 0; end"a单例类上定义了一个类方法。因此,a.pops实际上意味着调用pops类方法(再次,就像调用Klass.class_method一样)。

a.popsx不起作用,因为您首先必须创建a的实例才能在其上调用popsx(但不可能创建单例类的新实例)。

Klass.pops不起作用,因为Klass'单例类中没有定义任何pops方法(pops的单例类中定义了a)。< / p>

Klass.popsx有效,因为a.class_eval "def popsx; puts 1; end"已经定义了popsx实例方法,然后在Klass对象上调用该方法。在某种程度上,它类似于这个例子:

class Other
end

o = Other.new

Other.class_eval "def yeah; puts 'yeah'; end"

o.yeah  # yeah

希望它有所帮助。

答案 1 :(得分:3)

首先,虽然似乎 eigentclass 被某些人使用单身类是更常见的术语。 Singleton类包含Ruby中对象的特定于对象的行为。除了此单例类所属的原始对象外,您无法创建该类的其他实例。

在不同类型的eval this article中定义方法时,为instance_evalclass_eval中定义的方法引入了很好的规则:

Use ClassName.instance_eval to define class methods.
Use ClassName.class_eval to define instance methods.

这几乎描述了这种情况。

关于作为Class类实例的类,他们的单例类是Class类的子类以及其他一些疯狂的东西(与问题没有多大关系),有一篇文章。但是,由于您的问题可以很容易地应用于常规对象及其类(并且它使事情更容易解释),我决定删除所有(尽管如此,您仍然可以在修订历史中看到这些内容的答案)。

让我们看看该类的常规类和实例,看看它们是如何工作的:

class A; end
a = A.new

不同类型的eval中的方法定义:

# define instance method inside class context
A.class_eval { def bar; 'bar'; end }
puts a.bar     # => bar
puts A.new.bar # => bar

# class_eval is equivalent to re-opening the class
class A
  def bar2; 'bar2'; end
end
puts a.bar2     # => bar2
puts A.new.bar2 # => bar2

定义特定于对象的方法:

# define object-specific method in the context of object itself
a.instance_eval { def foo; 'foo'; end }
puts a.foo # => foo

# method definition inside instance_eval is equivalent to this
def a.foo2; 'foo2'; end
puts a.foo2 # => foo2

# no foo method here
# puts A.new.foo # => undefined method `foo' for #<A:0x8b35b20>

现在让我们看一下对象的单例类a

# singleton class of a is subclass of A
p (class << a; self; end).ancestors
# => [A, Object, Kernel, BasicObject]

# define instance method inside a's singleton class context
class << a
  def foobar; 'foobar'; end;
end
puts a.foobar # => foobar

# as expected foobar is not available for other instances of class A
# because it's instance method of a's singleton class and a is the only
# instance of that class
# puts A.new.foobar # => undefined method `foobar' for #<A:0x8b35b20>

# same for equivalent class_eval version
(class << a; self; end).class_eval do
  def foobar2; 'foobar2'; end;
end
puts a.foobar2 # => foobar2

# no foobar2 here as well
# puts A.new.foobar2 # => undefined method `foobar2' for #<A:0x8b35b20>

现在让我们看一下实例变量:

# define instance variable for object a
a.instance_eval { @x = 1 }

# we can access that @x using same instance_eval
puts a.instance_eval { @x } # => 1
# or via convenient instance_variable_get method
puts a.instance_variable_get(:@x) # => 1

现在class_eval中的实例变量:

# class_eval is instance method of Module class
# so it's not available for object a
# a.class_eval { } # => undefined method `class_eval' for #<A:0x8fbaa74>

# instance variable definition works the same inside
# class_eval and instance_eval
A.instance_eval { @y = 1 }
A.class_eval    { @z = 1 }

# both variables belong to A class itself
p A.instance_variables # => [:@y, :@z]

# instance variables can be accessed in both ways as well
puts A.instance_eval { @y } # => 1
puts A.class_eval    { @z } # => 1

# no instance_variables here
p A.new.instance_variables # => []

现在,如果您将类A替换为类Class,将对象a替换为对象Klass(在此特定情况下,仅仅是类{{1}的实例我希望你能解释一下你的问题。如果你还有一些随意问的话。