首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Ruby中的动态方法调用

Ruby中的动态方法调用
EN

Stack Overflow用户
提问于 2013-07-04 01:54:10
回答 4查看 30.9K关注 0票数 71

据我所知,在Ruby中有三种动态调用方法:

方法1:

代码语言:javascript
复制
s = SomeObject.new
method = s.method(:dynamic_method)
method.call

方法二:

代码语言:javascript
复制
s = SomeObject.new
s.send(:dynamic_method)

方法3:

代码语言:javascript
复制
s = SomeObject.new
eval "s.dynamic_method"

通过对它们进行基准测试,我已经确定了方法1到目前为止是最快的,方法2是最慢的,方法3是到目前为止最慢的。

我还发现.call.send都允许调用私有方法,而eval不允许。

所以我的问题是:有没有理由使用.sendeval?为什么你不总是使用最快的方法呢?这些调用动态方法的方法还有哪些不同之处?

EN

回答 4

Stack Overflow用户

发布于 2013-07-04 11:57:21

这样想吧:

方法1 (method.call):单一运行时

如果你直接在你的程序上运行一次Ruby,你就可以控制整个系统,并且你可以通过"method.call“的方法保持一个”指向你的方法的指针“。你所要做的就是抓住一个“活动代码”的句柄,你可以随时运行它。这基本上与直接从对象内部调用方法一样快(但它没有使用object.send快-请参阅其他答案中的基准测试)。

方法2 (object.send):将方法名称持久化到数据库

但是,如果您希望将要调用的方法的名称存储在数据库中,并且在将来的应用程序中,您希望通过在数据库中查找来调用该方法名称,该怎么办呢?然后,您将使用第二种方法,它会导致ruby使用第二个"s.send(:dynamic_method)“方法调用任意的方法名。

方法3 (eval):自修改方法代码

如果您想以一种将方法作为全新代码运行的方式编写/修改/持久化代码到数据库中,该怎么办?您可能会定期修改写入数据库的代码,并希望每次都将其作为新代码运行。在这种(非常不寻常的情况)中,您可能希望使用第三种方法,它允许您将方法代码写出为字符串,在以后的某个日期加载回来,并完整地运行它。

为了它的价值,通常在Ruby世界中,使用Eval (方法3)被认为是不好的形式,除非在非常,非常深奥和罕见的情况下。因此,对于您遇到的几乎所有问题,您都应该坚持使用方法1和方法2。

票数 12
EN

Stack Overflow用户

发布于 2017-06-02 04:33:56

下面是所有可能的方法调用:

代码语言:javascript
复制
require 'benchmark/ips'

class FooBar
  def name; end
end

el = FooBar.new

Benchmark.ips do |x|
  x.report('plain') { el.name }
  x.report('eval') { eval('el.name') }
  x.report('method call') { el.method(:name).call }
  x.report('send sym') { el.send(:name) }
  x.report('send str') { el.send('name') }
  x.compare!
end

结果是:

代码语言:javascript
复制
Warming up --------------------------------------
               plain   236.448k i/100ms
                eval    20.743k i/100ms
         method call   131.408k i/100ms
            send sym   205.491k i/100ms
            send str   168.137k i/100ms
Calculating -------------------------------------
               plain      9.150M (± 6.5%) i/s -     45.634M in   5.009566s
                eval    232.303k (± 5.4%) i/s -      1.162M in   5.015430s
         method call      2.602M (± 4.5%) i/s -     13.009M in   5.010535s
            send sym      6.729M (± 8.6%) i/s -     33.495M in   5.016481s
            send str      4.027M (± 5.7%) i/s -     20.176M in   5.027409s

Comparison:
               plain:  9149514.0 i/s
            send sym:  6729490.1 i/s - 1.36x  slower
            send str:  4026672.4 i/s - 2.27x  slower
         method call:  2601777.5 i/s - 3.52x  slower
                eval:   232302.6 i/s - 39.39x  slower

预计普通调用是最快的,没有任何额外的分配,符号查找,只是查找和评估方法。

至于通过符号的send,它比通过字符串更快,因为它更容易为符号分配内存。一旦它被定义,它就会在内存中存储很长时间,并且没有重新分配。

同样的原因也适用于method(:name) (1),它需要为Proc对象分配内存(2)我们在类中调用方法,这导致了额外的方法查找,这也需要时间。

eval是运行解释器的,所以它是最重的。

票数 5
EN

Stack Overflow用户

发布于 2015-03-19 05:25:49

我从@Stefan更新了基准测试,以检查在保存对方法的引用时是否有一些速度上的改进。但是,sendcall快得多

代码语言:javascript
复制
require 'benchmark'

class Foo
  def bar; end
end

foo = Foo.new
foo_bar = foo.method(:bar)

Benchmark.bm(4) do |b|
  b.report("send") { 1_000_000.times { foo.send(:bar) } }
  b.report("call") { 1_000_000.times { foo_bar.call } }
end

结果如下:

代码语言:javascript
复制
           user     system      total        real
send   0.080000   0.000000   0.080000 (  0.088685)
call   0.110000   0.000000   0.110000 (  0.108249)

因此,send似乎就是最好的选择。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17454992

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档