15

考虑以下类:

class Foo
  def an_inst_method
    'instance method'
  end

  def self.a_class_method
    'class method'
  end

  alias_method :a_new_inst_method, :an_inst_method
end

这不是问题,您可以毫无问题地拨打电话Foo.new.a_new_inst_method

我希望能够拥有一个类方法Foo.add_widget(*items)和别名,这样我就可以执行以下操作:

Foo.add_widget 'item1'
Foo.add_widgets 'item2', 'item3'

所以本质上它有一个“红宝石风格” 1.minute2.minutes所以我想给一个Foo.add_widget这样的调用起别名,调用Foo.add_widgets完全相同的方法。我知道我可以把它包起来,但我觉得我应该能够以一种更干净的方式做到这一点。

考虑一下我尝试这样的尝试:

class Foo
  def an_inst_method
    'instance method'
  end

  def self.a_class_method
    'class method'
  end

  alias_method :a_new_inst_method, :an_inst_method
  alias_method :a_new_class_method, :a_class_method
end

但是,我收到以下错误:

NameError (undefined method `a_class_method' for class `Foo')

所以看起来这不适用于类方法。我该怎么做呢?

4

5 回答 5

12

alias_method别名接收者的实例方法。类方法实际上是在类的单例类上定义的实例方法。

class MyClass
  def self.a
    "Hello World!"
  end
end

method_1 = MyClass.method(:a).unbind
method_2 = MyClass.singleton_class.instance_method(:a)

method_1 == method_2
#=> true

要为单例类上定义的实例方法设置别名,您可以使用class << object语法打开它。

class << MyClass
  alias_method :b, :a
end

MyClass.b
#=> "Hello World!"

或者你可以直接使用方法来引用它singleton_class

MyClass.singleton_class.alias_method :c, :a

MyClass.c
#=> "Hello World!"

如果您仍在类上下文中,self则将引用该类。所以上面也可以写成:

class MyClass
  class << self
    def a
      "Hello World!"
    end
    alias_method :b, :a
  end
end

或者

class MyClass
  def self.a
    "Hello World!"
  end
  singleton_class.alias_method :c, :a
end

或者两者的结合。

于 2020-04-20T20:18:06.603 回答
6

要理解的重要一点是,Ruby中没有类方法之类的东西。

类方法实际上只是一个单例方法。类方法没有什么特别之处。每个对象都可以有单例方法。当对象是 a 时,我们只称它们为“类方法”,Class因为“实例的单例方法Class”太长且笨拙。

等待!我说的是“单例方法”吗?

要理解的另一件重要的事情是,Ruby中没有单例方法之类的东西。

单例方法实际上只是单例类的常规无聊的旧实例方法。单例方法没有什么特别之处。它们只是实例方法,就像任何其他实例方法一样。

事实上,Ruby只有实例方法。没有函数,没有构造函数,没有静态方法,没有类方法,没有模块函数,没有单例方法。

问题不是“这是一个类方法吗,这是一个单例方法”,而是“这个方法定义在哪个模块中?”

“单例方法”实际上是在单例类中定义的实例方法。访问单例类的语法foo

class << foo
end

还有一种方法Object#singleton_class可以返回对象的单例类。

为什么我如此积极地强调每个方法都是实例方法而类方法不存在的事实?因为这意味着 Ruby 的对象模型比人们想象的要简单得多!毕竟,在您的问题中,您已经表明您知道如何为实例方法取别名,但您说您不知道如何为类方法取别名。但是,这是错误的!您确实知道如何给类方法起别名,因为它们只是实例方法。如果你被正确地教导了这个事实,你就不需要问这个问题了!

一旦你明白每个方法都是一个实例方法,并且我们所说的“单例方法”只是单例类的实例方法,那么解决方案就变得清晰了:

singleton_class.alias_method :a_new_class_method, :a_class_method

注意:当我在上面写“没有 X 之类的东西”时,我的意思是“ Ruby 语言中没有 X 之类的东西”。这并不意味着这些概念在 Ruby 社区中不存在。

我们经常谈论“单例方法”和“类方法”,仅仅是因为它比谈论“单例类的实例方法”或“恰好是该类的实例的对象的单例类的实例方法”更Class容易”。Ruby 核心库中甚至还有Object#define_singleton_method, Object#singleton_method, Object#singleton_methods, Module#private_class_method,Module#public_class_method和等方法。Module#module_function但记住这些不是语言概念总是很重要的。这些是社区概念,只存在于我们的脑海中以及某些库方法的名称中。

于 2020-04-21T06:07:46.707 回答
5

您可以将类方法及其alias_method调用包装到一个单独的模块中,并通过以下方式将该模块合并到您的类中extend

class Foo
  def an_inst_method
    'instance method'
  end
  alias_method :a_new_inst_method, :an_inst_method

  module ClassMethods
    def a_class_method
      'class method'
    end
    alias_method :a_new_class_method, :a_class_method
  end

  extend ClassMethods
end
于 2020-04-20T20:27:31.757 回答
2

alias_method对实例进行操作,因此您需要更深入一层并在Class实例或类的元类上进行操作。Ruby 有一个狂野的对象模型,它可能会让人脑洞大开,但它也给了你很多力量:

class Foo
  def an_inst_method
    'instance method'
  end

  alias_method :a_new_inst_method, :an_inst_method

  class << self
    def a_class_method
      'class method'
    end

    alias_method :a_new_class_method, :a_class_method
  end
end
于 2020-04-20T19:53:48.170 回答
2

好的,看起来我可以做这样的事情并且它会起作用:

class Foo
  def an_inst_method
    'instance method'
  end

  def self.a_class_method
    'class method'
  end

  alias_method :a_new_inst_method, :an_inst_method

  class << self
    alias_method :a_new_class_method, :a_class_method
  end
end

Foo.a_class_method # => "class method" 
Foo.a_new_class_method # => "class method"

如果有人在这里有任何关于 Ruby 语言的有用信息并想提交一个全面的答案,我会给你一个 +1。

于 2020-04-20T19:45:48.867 回答