什么是类中的自我<< self和为什么这与定义这段代码的Class实例不同?

时间:2016-06-02 17:34:15

标签: ruby

下面的代码尝试创建一个MyClass类,它将方法self.add的调用委托给调用方法self.newAdd返回的对象。

class MyAdd
      def add(a,b)
        a + b
      end
    end


class MyClass
  def self.newAdd
    MyAdd.new
  end

  def self.delegate(*methods, options)
    return unless options.has_key?(:to)

    methods.each do |method|
      define_method method, ->(*args, &prc) do
        delegated_to = self.send(options[:to])
        delegated_to.send(method, *args, &prc)
      end
    end
  end

  class << self
    debugger;
    delegate :add, to: :newAdd
  end
end

运行此代码时发生的错误是

  

NoMethodError:#<Class:MyClass>

的未定义方法'委托'

如果我导航到保存文件的目录并打开解释器,则执行在第四行到最后一行的调试器处停止。然后,我可以查看自己和MyClass的可用方法

require_relative './test.rb'
MyClass.methods - Object.methods  #[:newAdd, :delegate]
self.methods - Object.methods  #[:nesting]
self  # #<Class:MyClass>
self.class  # Class
MyClass.class  # Class

为什么selfMyClass范围内的class << self不一样?更具体地说,为什么方法delegateclass << self内无法自我使用?

3 个答案:

答案 0 :(得分:3)

  

为什么self与类中的MyClass不同?&lt;&lt;自我范围。

因为class关键字总是改变范围:

class MyClass
  puts self  #=> MyClass

  class <<self
    puts self  #=>MyClass’s singleton class
  end
end
  

更具体地说,为什么方法委托对自己不可用   在课堂内&lt;&lt;自?

class MyClass

  def self.delegate
    puts "executing MyClass.delegate()"
  end

  class <<self
    delegate 
  end

end

--output:--
1.rb:8:in `singleton class': undefined local variable or method `delegate' for #<Class:MyClass> (NameError)
  from 1.rb:7:in `<class:MyClass>'
  from 1.rb:1:in `<main>'

请注意,以下结构是等效的:

class MyClass

  def self.delegate
    puts "executing MyClass.delegate()"
  end

end

MyClass.delegate

--output:--
executing MyClass.delegate()

class MyClass

  class <<self
    def delegate
      puts "executing MyClass.delegate()"
    end
  end

end

MyClass.delegate

--output:--
executing MyClass.delegate()

因此,您的代码相当于:

class MyClass

  class <<self
    def delegate
      puts "executing MyClass.delegate()"
    end

    delegate
  end

end

如果你暂时忽略外部MyClass,那么你定义了一个这样的类:

class <<self
  def delegate
    puts "executing MyClass.delegate()"
  end

  delegate
end

可以像这样复制相同的结构:

class Dog
  def bark
    puts “woof”
  end

  bark
end

会产生相同类型的错误:

1.rb:7:in `<class:Dog>': undefined local variable or method `bark' for Dog:Class (NameError)
    from 1.rb:1:in `<main>'
  1. 当你调用一个方法而你没有指定一个接收者时,ruby使用当前分配给自变量的任何对象作为接收者。

  2. 在方法中,ruby将调用方法的对象分配给自变量。调用方法的对象与定义方法的类(对象)不同。

  3. 在类中,但在任何方法定义之外,ruby会将类(对象)分配给self。

  4. 请注意,Dog类的实例可以调用Dog类中的def,例如:吠()。类似地,单例类的实例可以在单例类中调用def,例如, delegate() - 单例类本身不能在单例类中调用def。它们被称为单例类的全部原因是因为单例类只有一个实例 - 在您的情况下,单例类的一个实例是MyClass。因此,MyClass可以调用delegate(),但是单例类不能调用delegate()。

      

    我真的不明白eignclass上的类方法是什么   但是。

    就个人而言,我不使用eigenclass这个词。在我看来,ruby已经决定该术语是singleton class。如果查看Object类的文档,则其中没有包含eigenclass的方法名称,但其中包含singleton class的方法名称。

    所有对象都有一个单例类。单例类是一个对象。因此,每个单例类也都有一个单例类 - 这意味着单例类的链是无限的:

    class Dog
    end
    
    s1 = Dog.singleton_class
    puts s1  
    
    s2 = s1.singleton_class
    puts s2
    
    s3 = s2.singleton_class
    puts s3
    
    --output:--
    #<Class:Dog>
    #<Class:#<Class:Dog>>
    #<Class:#<Class:#<Class:Dog>>>
    

    这意味着你可以做这样的事情:

    class Dog
      class <<self  #s1
        class <<self #s2
          def greet  #Instances of s2 can call greet, and the only instance of s2 is s1.
            puts "hello"
          end
        end
      end
    end
    
    
    class Dog
      class <<self
        #Inside here self = Dog's singleton class = s1
        greet  #equivalent to Dogs_singleton_class.greet
      end
    end
    
    --output:--
    hello
    

    但是,我从未见过有人在代码中使用单例类(s2)的单例类。很久很久以前我曾经做过一次回答问题,没有人知道我在说什么。

    有一些方法查找图here,这可能证明是有用的。

答案 1 :(得分:1)

以下是代码的重新设计部分,无误地运行:

class MyClass
  def self.newAdd
    MyAdd.new
  end

  def self.delegate(*methods, options)
    return unless options.has_key?(:to)

    methods.each do |method|
      target = self

      define_method method, ->(*args, &prc) do
        delegated_to = target.send(options[:to])
        delegated_to.send(method, *args, &prc)
      end
    end
  end

  delegate :add, to: :newAdd
end

作为一个注释,请尝试指出像new_add这样的命名方法,并避免在其中使用大写字母。

您无法使用const_get来检索方法。您正在寻找的是method,但这并非总是最好的计划。 send通常就足够了。

答案 2 :(得分:0)

我发布的实际可行的代码版本如下:

class MyAdd
  def add(a,b)
    a + b
  end
end

module Delegate
  def delegate(*methods, options)
    return unless options.has_key?(:to)

    # methods are the methods that should be delegated to 
    # the object returned by calling the method passed in options[:to]
    methods.each do |method|
      define_method method, ->(*args, &prc) do
        delegated_to = self.send(options[:to])
        delegated_to.send(method, *args, &prc)
      end
    end
  end
end

class MyClass
  extend Delegate

  def self.newAdd
    MyAdd.new
  end

  class << self
    extend Delegate
    delegate :add, to: :newAdd
  end
end

现在,如果我们从delegate :add, to: :newAdd内拨打class << self,我们会将类方法add的调用委托给类方法newAdd。这样做的原因是在class << self内部,self是类MyClass,我们所在的范围selfMyClass的单例类。通过在这里扩展模块Delegate,我们在这个单例类上定义了一个类方法。该方法称为委托,并在其中调用define_method,它在单例类(例如,add)上创建实例方法,因此它们是原始类(MyClass)上的类方法。

如果我们从类MyClass语句中调用delegate,我们会将实例方法add的调用委托给实例方法newAdd(如果存在)。原因是在class MyClass创建的范围内,self是MyClass,通过在此处扩展模块Delegate,我们创建了一个类方法delegate(MyClass上的实例方法delegate& #39; s单身类)。这个类方法调用define_method,它在MyClass上创建实例方法(例如,添加)。

我们最终得到了两个名为delegate的方法,一个是MyClass的类方法,另一个是MyClass的单​​例类的类方法。