什么`&方法(:method_ name)`在ruby中意味着什么?

时间:2017-07-10 13:33:32

标签: ruby metaprogramming

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

以下是我的第一次尝试:

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

我不完全理解这里发生的事情:&method(:private_class_greeter)

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

如果我要替换:

define_method :public_instance_greeter, &method(:private_class_greeter)

使用:

def public_instance_greeter
  XYZ
end
那么,代替XYZ的内容应该是什么?

2 个答案:

答案 0 :(得分:9)

Ruby如何解析&method(:private_class_greeter)

表达式&method(:private_class_greeter)

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

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.

什么是&运算符?

& 运算符用于Proc作为块传递给方法,该方法需要将块传递给它。它还隐式调用您传入的值的to_proc方法,以便将非Proc的值转换为Proc

Method类实现to_proc - 它将方法的内容作为Proc返回。因此,您可以使用Method&实例添加前缀,并将其作为块传递给另一个方法:

def call_block
  yield
end

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

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

这是&: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) }

如何复制&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

答案 1 :(得分:3)

首先,请妥善保管您的缩进。 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