【问题标题】:What does `&method(:method_ name)` mean in ruby?`&method(:method_name)` 在 ruby​​ 中是什么意思?
【发布时间】:2017-07-10 13:33:32
【问题描述】:

我试图创建一个具有私有类方法的类。我希望这个私有类方法可以在实例方法中使用。

以下是我的第一次尝试:

class Animal
  class << self
    def public_class_greeter(name)
      private_class_greeter(name)
    end

  private
    def private_class_greeter(name)
      puts "#{name} greets private class method"
    end
  end

  def public_instance_greeter(name)
    self.class.private_class_greeter(name)
  end
end

Animal.public_class_greeter('John') 工作正常,打印John greets private class method

但是,Animal.new.public_instance_greeter("John") 会引发错误:NoMethodError: private method 'private_class_greeter' called for Animal:Class

这是意料之中的,因为调用self.class.private_class_greeterAnimal.private_class_greeter 相同,显然会引发错误。

在搜索了如何解决这个问题之后,我想出了以下代码,它可以完成这项工作:

class Animal
  class << self
    def public_class_greeter(name)
      private_class_greeter(name)
    end

  private
    def private_class_greeter(name)
      puts "#{name} greets private class method"
    end
  end

  define_method :public_instance_greeter, &method(:private_class_greeter)
end

我不太明白这里发生了什么:&amp;method(:private_class_greeter)

你能解释一下这是什么意思吗?

如果我要替换:

define_method :public_instance_greeter, &method(:private_class_greeter)

与:

def public_instance_greeter
  XYZ
end

那么,XYZ 的内容应该是什么?

【问题讨论】:

    标签: ruby metaprogramming


    【解决方案1】:

    Ruby 如何解析&amp;method(:private_class_greeter)

    表达式&amp;method(:private_class_greeter)

    • 方法调用的值method(:private_class_greeter)
    • &amp; 运算符为前缀。

    method 方法有什么作用?

    method 方法在当前上下文中查找指定的方法名,并返回一个代表它的Method 对象。 irb 中的示例:

    def foo
      "bar"
    end
    
    my_method = method(:foo)
    #=> #<Method: Object#foo>
    

    一旦你有了这个方法,你就可以用它做各种事情:

    my_method.call
    #=> "bar"
    
    my_method.source_location   # gives you the file and line the method was defined on
    #=> ["(irb)", 5]
    
    # etc.
    

    &amp; 运算符有什么用?

    &amp; 运算符用于Proc 作为块传递给期望将块传递给它的方法。它还对您传入的值隐式调用to_proc 方法,以便将不是Proc 的值转换为Proc

    Method 类实现了to_proc — 它以Proc 的形式返回方法的内容。因此,您可以在 Method 实例前面加上 &amp; 并将其作为块传递给另一个方法:

    def call_block
      yield
    end
    
    call_block &my_method   # same as `call_block &my_method.to_proc`
    #=> "bar"
    

    define_method 方法恰好采用了一个块,其中包含正在定义的新方法的内容。在您的示例中,&amp;method(:private_class_greeter) 将现有的 private_class_greeter 方法作为块传递。


    &amp;:symbol 是这样工作的吗?

    是的。 Symbol 实现了to_proc,以便您可以像这样简化代码:

    ["foo", "bar"].map(&:upcase)
    #=> ["FOO", "BAR"]
    
    # this is equivalent to:
    ["foo", "bar"].map { |item| item.upcase }
    
    # because
    :upcase.to_proc
    
    # returns this proc:
    Proc { |val| val.send(:upcase) }
    

    如何复制&amp;method(:private_class_greeter)

    你可以传入一个调用目标方法的块:

    define_method :public_instance_greeter do |name|
      self.class.send(:private_class_greeter, name)
    end
    

    当然,那么你就不需要再使用define_method了,这导致了Eric在his answer中提到的相同的解决方案:

    def public_instance_greeter(name)
      self.class.send(:private_class_greeter, name)
    end
    

    【讨论】:

    • 解释得很好。
    • &amp; Method 实例作为块传递。它根本不按类型区分参数。它在接收器上隐式调用to_proc(在这种情况下为Method 实例)并将结果(Proc 实例)传递给方法。与&amp;:to_s:to_s 符号上调用Symbol#to_proc 并进一步传递Proc 完全一样。
    • 我是故意简化解释的。但我想我可以添加一些关于to_proc的解释...
    • 好吧,“&amp; 运算符用于将 [..] 方法作为块传递”的措辞是错误的,这就是为什么我建议至少以某种方式提及 to_proc
    • 好收获!更新了更准确的答案。
    【解决方案2】:

    首先,请注意缩进。 private 应该是右边的 2 个空格:它给人的印象是 public_instance_greeter 是私有的。

    如果你不关心封装,你可以简单地使用Kernel#send

    class Animal
      class << self
        def public_class_greeter(name)
          private_class_greeter(name)
        end
    
        private
        def private_class_greeter(name)
          puts "#{name} greets private class method"
        end
      end
    
      def public_instance_greeter(name)
        self.class.send(:private_class_greeter, name)
      end
    end
    
    Animal.public_class_greeter('John')
    # John greets private class method
    Animal.new.public_instance_greeter("John")
    # John greets private class method
    

    【讨论】:

    • 感谢您对缩进的建议。我知道流行的风格指南。我认为负缩进 private 关键字有助于轻松识别私有方法的开始位置。这就是为什么我更喜欢将 private 关键字缩进到根级别的原因。我知道这违反了大多数风格指南。你认为我的思考过程有意义吗?
    • @SatyaramBV:如果您(和您的同事)可以阅读它,那对我来说很好。避免此问题的一种方法是将 `class Animal 类的末尾。
    • @SatyaramBV FWIW,我工作过的所有团队也都更喜欢突出的可访问性关键字。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-29
    • 2016-08-17
    • 2011-10-14
    • 2014-06-21
    • 2011-04-18
    • 1970-01-01
    • 2010-09-18
    相关资源
    最近更新 更多