49 votes

Quelle est la différence entre class_eval, class_exec, module_eval et module_exec ?

Je lis le Module mais ils ne parviennent pas à comprendre leurs différences et à savoir lequel doit être utilisé à quel endroit.

Comment le eval différent de exec ?

90voto

Marc-André Lafortune Points 34140

Je vais répondre à un peu plus que votre question en incluant instance_{eval|exec} dans votre question.

Toutes les variantes de {instance|module|class}_{eval|exec} changer le contexte actuel c'est-à-dire la valeur de self :

class Array
  p self                     # prints "Array"
  43.instance_eval{ p self } # prints "43"
end

Maintenant, les différences. Le site eval accepte une chaîne ou un bloc, tandis que la version exec n'acceptent qu'un bloc mais vous permettent de lui passer des paramètres :

def example(&block)
  42.instance_exec("Hello", &block)
end
example{|mess| p mess, self } # Prints "Hello" then "42"

El eval ne permet pas de passer des paramètres. Elle fournit self comme premier paramètre, bien que je ne puisse pas penser à une utilisation pour cela.

Enfin, module_{eval|exec} est le même que celui correspondant class_{eval|exec} mais ils sont légèrement différents de instance_{eval|exec} car ils changent ce qui est la classe ouverte actuelle (c'est-à-dire ce qui sera affecté par def ) de différentes manières :

String.instance_eval{ def foo; end }
Integer.class_eval  { def bar; end }

String.method_defined?(:foo)            # => false
String.singleton_methods.include?(:foo) # => true
Integer.method_defined?(:bar)           # => true

Alors obj.instance_{eval|exec} ouvre la classe singleton de obj alors que mod.{class|module}_{eval|exec} ouvre mod lui-même.

Bien sûr, instance_{eval|exec} sont disponibles sur n'importe quel objet Ruby (y compris les modules), alors que {class|module}_* sont uniquement disponibles sur Module (et donc Classes )

7voto

tjarratt Points 1321

Pour répondre d'abord à votre dernière question, l'évaluation (dans toutes ses variantes) est complètement différent de l'exécution. exec $command démarre un nouveau processus pour exécuter la commande que vous avez spécifiée et se termine à la fin de ce processus.

class_eval y module_eval ont le pouvoir de redéfinir les classes et les modules - même ceux que vous n'avez pas écrits vous-même. Par exemple, vous pouvez utiliser la classe eval pour ajouter une nouvelle méthode qui n'existait pas.

Fixnum.class_eval { def number; self; end }
7.number # returns '7'

class_eval peut être utilisé pour ajouter des méthodes d'instance, et instance_eval peut être utilisé pour ajouter des méthodes de classe (oui, cette partie est très confuse). Une méthode de classe serait quelque chose comme Thing.foo -- vous appelez littéralement le foo sur la méthode Thing classe. Une méthode d'instance est comme l'exemple ci-dessus, utilisant class_eval J'ai ajouté un number à chaque instance de Fixnum .

Ok, donc c'est le *_eval classe de méthodes. Les méthodes exec sont similaires, mais elles vous permettent de regarder à l'intérieur d'une classe et d'exécuter un bloc de code comme s'il était défini comme une méthode sur cette classe. Vous avez peut-être une classe qui ressemble à ceci :

class Foo
  @@secret = 'secret key'
  @@protected = 'some secret value'
  def protected(key)
    if key == @@secret
       return @@protected
    end
  end
end

La classe Foo est juste une enveloppe autour d'une valeur secrète, si vous connaissez la bonne clé. Toutefois, vous pouvez inciter la classe à vous livrer ses secrets en exécutant un bloc dans le contexte de la classe, comme suit :

Foo.class_exec { @@secret = 'i'm a hacker' }
Foo.protected('i'm a hacker') #returns the value of @@protected because we overwrote @@secret

En général, avec beaucoup d'outils dans Ruby, vous pouvez utiliser n'importe lequel d'entre eux pour résoudre beaucoup de problèmes. La plupart du temps, vous n'en aurez probablement même pas besoin, à moins que vous ne vouliez patcher une classe définie par une bibliothèque que vous utilisez (bien que cela ouvre toute une boîte de vers). Essayez de jouer avec eux dans irb et voyez lequel vous trouvez le plus facile. Personnellement, je n'utilise pas le *_exec autant que les *_eval méthodes, mais c'est une préférence personnelle de ma part.

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