68 votes

Méthode dynamique appelant en Ruby

Pour autant que je suis au courant il y a trois façons d'appeler de manière dynamique une méthode en Ruby:

Méthode 1:

s = SomeObject.new
method = s.method(:dynamic_method)
method.call

Méthode 2:

s = SomeObject.new
s.send(:dynamic_method)

Méthode 3:

s = SomeObject.new
eval "s.dynamic_method"

Par l'analyse comparative entre eux, j'ai établi que la Méthode 1 est de loin la manière la plus rapide, la Méthode 2 est plus lent, et 3 de la Méthode est de loin la plus lente.

J'ai aussi constaté que .call et .send à la fois permettre d'appeler des méthodes privées, tandis que d' eval ne le sont pas.

Donc ma question est: est-il une raison à jamais les utiliser, .send ou eval? Pourquoi voudriez-vous ne pas toujours utiliser la méthode la plus rapide? Quelles sont les autres différences entre ces méthodes de l'appel de méthodes dynamiques?

63voto

Stefan Points 23363

est-il une raison à jamais les utiliser, send?

call besoin d'une méthode d'objet, send n'est pas:

class Foo
  def method_missing(name)
    "#{name} called"
  end
end

Foo.new.send(:bar)         #=> "bar called"
Foo.new.method(:bar).call  #=> undefined method `bar' for class `Foo' (NameError)

est-il une raison à jamais les utiliser, eval?

eval évalue les expressions arbitraires, ce n'est pas seulement pour l'appel d'une méthode.


Concernant les points de référence, send semble être plus rapide que l' method + call:

require 'benchmark'

class Foo
  def bar; end
end

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

Résultat:

           user     system      total        real
send   0.210000   0.000000   0.210000 (  0.215181)
call   0.740000   0.000000   0.740000 (  0.739262)

12voto

Steve Midgley Points 435

Pensez-y de cette façon:

Méthode 1: Seul au moment de l'exécution

Si vous exécutez Ruby une fois sur votre programme en ligne droite à travers vous permet de contrôler l'ensemble du système et vous pouvez tenir sur un "pointeur de votre méthode" via la "méthode.appel". Tout ce que vous faites est maintenant sur une poignée de vivre "code" que vous pouvez exécuter chaque fois que vous le souhaitez. C'est fondamentalement aussi vite que l'appel de la méthode à partir de l'intérieur de l'objet (il pourrait effectivement être plus rapide que je ne l'ai pas testé).

Méthode 2: Persistent nom de la méthode à la base de données

Mais que faire si vous voulez stocker le nom de la méthode que vous voulez l'appeler, dans une base de données et dans une future application que vous souhaitez appeler le nom de la méthode en regardant dans la base de données? Ensuite, vous utilisez la deuxième approche, qui provoque ruby pour appeler une méthode arbitraire nom à l'aide de votre deuxième "s.envoyer(:dynamic_method)" approche.

Méthode 3: Auto-modifiant le code de la méthode

Que faire si vous voulez écrire/modifier/persistent code à une base de données d'une manière qui va exécuter la méthode comme la marque d'un nouveau code? Vous pourriez périodiquement modifier le code écrit à la base de données et souhaitez exécuter en tant qu'nouveau code à chaque fois. Dans ce (très rare cas) que vous souhaitez utiliser votre troisième approche, qui permet d'écrire votre code de la méthode comme une chaîne de caractères, de le charger de nouveau à une date ultérieure, et l'exécuter dans son intégralité.

Pour ce que ça vaut, il est généralement considéré dans l'Ruby monde comme de la mauvaise forme à utiliser Eval (méthode 3) sauf dans de très, très ésotérique et de rares cas. Donc, vous devriez vraiment coller avec les méthodes 1 et 2 pour presque tous les problèmes que vous rencontrez.

0voto

sawa Points 62592

Le point de l'ensemble de l' send et eval , c'est que vous pouvez changer la commande de façon dynamique. Si la méthode que vous voulez exécuter est fixe, alors vous pouvez câbles de cette méthode sans l'aide d' send ou eval.

receiver.fixed_method(argument)

Mais quand vous voulez appeler une méthode qui varie ou vous ne savez pas à l'avance, alors vous ne pouvez pas écrire directement. D'où l'utilisation de l' send ou eval.

receiver.send(method_that_changes_dynamically, argument)
eval "#{code_to_evaluate_that_changes_more_dramatically}"

L'utilisation supplémentaire d' send , c'est que, comme vous l'avez remarqué, vous pouvez appeler une méthode explicite récepteur à l'aide de send.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X