在Ruby中如何将添加的方法引用到现有的类?

ehxuflar  于 2023-02-12  发布在  Ruby
关注(0)|答案(4)|浏览(184)

我用一个方法扩展了String类,比如说"foo"。

class String
  def foo
    puts "Hello World."
  end
end

为什么不能调用String.fooString.method("foo")?当我尝试调用时,得到了 * NoMethodError *。
我的最终目标是将foo传递给另一个方法,类似于bar(String.method('foo'))
先谢了

yzxexxkh

yzxexxkh1#

foo是一个示例方法,可以使用Module#instance_method获取方法,然后使用.bind(string)将其绑定到String对象,然后使用.call(args)调用该对象。

class String
  def foo
    puts "Hello #{self}."
  end
end

p String.instance_method(:foo)
p String.instance_method(:foo).bind("World")
String.instance_method(:foo).bind("World").call

输出:

#<UnboundMethod: String#foo() a.rb:2>
#<Method: String#foo() a.rb:2>
Hello World.
4jb9z9bj

4jb9z9bj2#

如果你真的希望它是一个类方法,你可以使用self.methodname来定义它:

class String
  def self.foo
    puts "Hello"
  end
end

String.foo    # => Hello

你也可以让它返回一个proc,这样它在其他地方也可以使用:

class String
  def self.foo = -> { puts "Hello" }
end

String.foo    # => #<Proc:0x0000000102a05ea8 (irb):14 (lambda)>
String.foo[]  # => Hello

带参数:

class String
  def self.foo = ->(name){ puts "Hello #{name}" }
end

String.foo["Malte"]  # => Hello Malte
monwx1rj

monwx1rj3#

您正在定义示例方法,但尝试调用静态方法。如果调用方式正确,则该方法将按预期工作。

class String
  def foo
    puts "Hello World."
  end
end

String.new("test").foo # output: Hello World.
mkh04yzy

mkh04yzy4#

你的第一个问题是"如果我执行

class String
  def foo
    puts "Hello World."
  end
end

为什么不能调用String.fooString.method("foo")
当你执行

String.foo
  #=> NoMethodError: undefined method 'foo' for String:Class

错误消息1非常具体:类String没有方法foo。这与下面的一致。

String.methods.include?(:foo)
  #=> false

你所做的就是创建一个类string的示例方法foo,让我们以类String的示例"cat"为例:

"cat".methods.include?(:foo)
  #=> true

"cat".foo
  #=> "Hello World."

我们还可以看看

String.instance_methods
  #=> [:unicode_normalize, :unicode_normalize!, :ascii_only?,
  #    :to_r,..., :foo, :count,..., :instance_exec]

或者为了更容易地在String的187个左右的示例方法中找到:foo,我们可以检查

String.instance_methods.sort
  #=> [:!, :!=,..., :extend, :foo, :force_encoding,..., :yield_self]

参见内核#方法和模块#示例_方法
如果要在String上调用foo,则需要编写

class String
  def self.foo
    puts "Hello World."
  end
end

String.foo
  #=> "Hello World."

当构造方法时,self等于String,因此上式相同

class String
  def String.foo
    puts "Hello World."
  end
end

通常使用self代替字面类名的原因是,它允许在不更改方法定义的情况下更改类名(我们显然不会重命名String,但我们可能希望更改用户定义的类名(例如,将Amount更改为Quantity)。
实际上,Ruby只有示例方法,所以让我们看看def String.foo在示例方法方面的含义。
我们看到

String.class
  #=> Class

这意味着StringClass的一个示例。

class Class
  def foo
    puts "Hello World."
  end
end
 
String.foo
  #=> "Hello World."

但这会产生不希望的效果,即使该示例方法Class对所有类都可用:

Array.foo
  #=> "Hello World."
Hash.foo
  #=> "Hello World."

相反,我们希望将foo的可用性限制为仅Class的一个示例String
每个Ruby对象都有一个特殊的类,叫做"Singleton类"(Singleton类是常用的几个名称之一),我说"特殊"是因为它不同于常规类,因为它不能被子类化,也不能创建示例。
我们可以编写以下代码来创建一个可以在String上调用的方法foo

string_single = String.singleton_class
  #=> #<Class:String>

string_single.define_method(:foo) { puts "Hello World." }
  #=> :foo

string_single.instance_methods.include?(:foo)
  #=> true

String.foo
  #=> Hello World.

参见模块#define_method。
事实上

def String.foo
  puts "Hello World."
end

只是上面使用define_method的简写。类似地,熟悉的

class String
  def foo
    puts "Hello World."
  end
end
  #=> :foo

只是一个缩写

String.define_method(:foo) { puts "Hello World." }
  #=> :foo

"cat".foo
  #=> "Hello World."

问题的第二部分问的是如何将一个单例方法传递给另一个单例方法。这很简单。我们已经在String的单例类上定义了foo,所以让我们定义另一个bar,它调用foo

String.singleton_class.define_method(:bar) { foo }
  #=> :bar

String.bar
  #=> Hello World.

1.注意错误信息的完整性,通常它们会指出问题所在。

相关问题