【问题标题】:Why can't I call include from a class method in ruby?为什么我不能从 ruby​​ 的类方法中调用 include?
【发布时间】:2011-09-03 21:11:45
【问题描述】:

您可以调用 include 将模块与 ruby​​ 中的类混合,但必须在类定义的开头完成。为什么不能在类函数中完成?有其他语法吗?

前:

module UsefulThings
  def a() puts "a" end
end

class IncludeTester
  include UsefulThings
    def initialize
  end
end

n = IncludeTester.new
n.a()

^^ 这可行,但如果我将 IncludeTester 更改为以下内容,我会收到错误“未定义的方法 `include'”

class IncludeTester
  def initialize
    include UsefulThings
  end
end

【问题讨论】:

    标签: ruby


    【解决方案1】:

    可以在类方法中完成。

    这行得通:

    module UsefulThings
      def a
        puts "a" 
      end
    end
    
    class IncludeTester
      def self.mix_in_useful_things
        include UsefulThings
      end 
    end
    
    x = IncludeTester.new
    
    IncludeTester.mix_in_useful_things
    
    x.a # => a
    

    但是“初始化”不是类方法,而是实例方法。

    “new”是一个类方法。您可以将 new 视为分配一个新对象,然后在其上调用 initialize,将传递给 new 的任何参数都传递给 initialize。

    您不能直接在初始化中调用 include,因为 include 是 Class 的私有方法(继承自 Module),而不是新创建的 IncludeTester 实例的私有方法。

    如果你想通过实例方法将模块包含到类中,你必须这样做:

    class IncludeTester
      def initialize
        self.class.send(:include, UsefulThings)
      end
    end
    

    这里必须使用“send”,因为 include 是私有方法,这意味着它只能通过隐式接收者(self)直接调用。

    当您在类定义中正常调用初始化时,实际上是使用“self”的隐式接收器调用它,指的是正在定义的类。

    这是您执行此操作时实际发生的情况:

    class IncludeTester
      include UsefulThings
    end
    

    【讨论】:

    • 是的,这非常有帮助(就像其他答案一样)。
    【解决方案2】:

    include 是 Module 的方法,Module 是 Class 的超类,因此 include 是 Class 上的方法,这使它成为 IncludeTester 中的类方法。当你这样做时:

    class IncludeTester
      def initialize
        include UsefulThings
      end
    end
    

    你试图在一个实例方法中调用一个类方法,而 Ruby 说

    `initialize':未定义的方法`include'

    因为没有名为include 的实例方法。如果你想在实例方法中调用类方法(例如initialize),你可以这样做:

    def initialize
      self.class.include UsefulThings
    end
    

    但这行不通,因为include 是私有方法;你可以通过class_eval 解决这个问题:

    def initialize
      self.class.class_eval {
        include UsefulThings
      }
    end
    

    每次实例化 IncludeTester 时都会使用 include UsefulThings,除了没有多大意义之外,如果 UsefulThings 具有 included 方法,则可能会导致问题。

    【讨论】:

    • 绕过私有方法访问保护的惯用方法是使用send,而不是任何eval 方法。
    【解决方案3】:

    实际上完全可以从类方法中包含一个模块,如下所示:

    module Stuff
      def say_hello
        puts "hello"
      end
    end
    
    class Foo
      def self.i_am_a_class_method
        include Stuff
      end
    
      def i_am_an_instance_method
      end
    end
    

    但是,您不能通过实例方法执行此操作,因为 include method 只能作为私有类方法使用,因此无法从 Foo.new 实例访问。

    【讨论】:

      【解决方案4】:

      你想要extend 方法:

      class IncludeTester
        def initialize
          extend UsefulThings
        end
      end
      

      这也不需要在 a 方法中完成:

      IncludeTester.new.tap { |newTester| newTester.extend(UsefulThings) }
      

      【讨论】:

      • 几乎!从实例方法调用“扩展”不会将模块混合到实例的类中——它会导致将不可见的“幽灵”类添加到对象继承链的开头(在实际类之前)并混合模块进入那个。像这样混合一个模块不会影响同一类的其他对象。
      猜你喜欢
      • 2022-10-04
      • 2011-03-12
      • 1970-01-01
      • 1970-01-01
      • 2023-01-13
      • 1970-01-01
      • 2015-03-09
      • 2016-01-27
      相关资源
      最近更新 更多