Question

class Hello
@hello = "hello"
    def display
        puts @hello
    end
end

h = Hello.new
h.display

J'ai créé la classe ci-dessus. Cela n'imprime rien. Je pensais que la variable d'instance @hello avait été définie lors de la déclaration de classe. Mais lorsque j'appelle la méthode d'affichage, la sortie est «nulle». Quelle est la bonne façon de faire cela?

Était-ce utile?

La solution

Les variables d’instance dans ruby ??peuvent être un peu déroutantes lors de la première utilisation de Ruby, en particulier si vous êtes habitué à un autre langage OO tel que Java.

Vous ne pouvez pas simplement déclarer une variable d'instance.

L'une des choses les plus importantes à savoir sur les variables d'instance dans ruby, à l'exception de la notation avec un préfixe @, est que elles prennent vie dès la première affectation .

class Hello
  def create_some_state
    @hello = "hello"
  end
end

h = Hello.new
p h.instance_variables 

h.create_some_state
p h.instance_variables

# Output
[]
["@hello"]

Vous pouvez utiliser la méthode Objet # variables_instance pour répertorier toutes les variables d'instance d'un objet.

Vous déclarez & # 8220; normalement & # 8221; et initialiser toutes les variables d’instance dans la méthode initialize. Une autre façon de documenter clairement les variables d’instance devant être accessibles au public consiste à utiliser les méthodes de module attr_accessor (lecture / écriture), attr_writer (écriture) et attr_reader (lire). Ces méthodes synthétiseront différentes méthodes d'accès pour la variable d'instance répertoriée.

class Hello
  attr_accessor :hello
end

h = Hello.new
p h.instance_variables 

h.hello = "hello"
p h.instance_variables

# Output
[]
["@hello"]

La variable d'instance n'est toujours pas créée tant qu'elle n'est pas affectée à l'aide de la méthode Hello # hello = synthétisée.

Un autre problème important, tel que décrit par kch, est que vous devez être conscient des différents contextes actifs lors de la déclaration d'une classe. Lors de la déclaration d'une classe, le destinataire par défaut (self) dans l'étendue la plus externe sera l'objet qui représente la classe elle-même. Par conséquent, votre code créera d’abord une variable d’instance de classe lors de l’affectation à @hello au niveau de la classe.

Inside méthodes self sera l'objet sur lequel la méthode est invoquée. Par conséquent, vous essayez d'imprimer la valeur d'une variable d'instance portant le nom @hello dans le répertoire. objet, qui n'existe pas (notez qu'il est parfaitement légal de lire une variable d'instance non existante).

Autres conseils

Vous devez ajouter une méthode initialize :

class Hello
    def initialize
        @hello = "hello"
    end
    def display
        puts @hello
    end
end

h = Hello.new
h.display

Le premier @hello de votre code s'appelle une variable d'instance de classe.

C'est une variable d'instance de l'objet de classe vers laquelle la constante Hello pointe. (et qui est une instance de la classe Class .)

Techniquement, lorsque vous vous trouvez dans la portée class , votre self est défini sur l'objet de votre classe actuelle et @variables . appartiennent à votre individu actuel. Garçon, je suis nul à expliquer ces choses.

Vous pouvez obtenir tout cela et bien plus de précisions en regardant cette collection de screencasts à 5 $ chacun de The Pragmatic Programmers .

(Ou vous pouvez demander des clarifications ici et je vais essayer de mettre à jour.)

il existe une description claire dans le livre "Le langage de programmation ruby", lisez-le, ce sera très utile. Je le colle ici (du chapitre 7.1.16):

  

Une variable d'instance utilisée dans une définition de classe mais en dehors d'un   La définition de la méthode d'instance est une variable d'instance de classe .

class Point
    # Initialize our class instance variables in the class definition itself
    @n = 0              # How many points have been created
    @totalX = 0         # The sum of all X coordinates
    @totalY = 0         # The sum of all Y coordinates

    def initialize(x,y) # Initialize method 
      @x,@y = x, y      # Sets initial values for instance variables
    end

    def self.new(x,y)   # Class method to create new Point objects
      # Use the class instance variables in this class method to collect data
      @n += 1           # Keep track of how many Points have been created
      @totalX += x      # Add these coordinates to the totals
      @totalY += y

      super             # Invoke the real definition of new to create a Point
                    # More about super later in the chapter
    end

    # A class method to report the data we collected
    def self.report
        # Here we use the class instance variables in a class method
        puts "Number of points created: #@n"
        puts "Average X coordinate: #{@totalX.to_f/@n}"
        puts "Average Y coordinate: #{@totalY.to_f/@n}"
    end
end

......

  

Parce que les variables d'instance de classe ne sont que des variables d'instance de classe   objets, nous pouvons utiliser attr, attr_reader et attr_accessor pour créer   méthodes d'accès pour eux.

class << self
  attr_accessor :n, :totalX, :totalY
end

Ces accesseurs étant définis, nous pouvons nous référer à nos données brutes sous les noms Point.n, Point.totalX et Point.totalY.

J'avais oublié qu'il y avait une "variable d'instance de classe" concept en rubis. En tout état de cause, le problème du PO semblait déroutant et ne figurait dans aucune des réponses précédentes, à l'exception d'un indice dans la réponse de kch: c'est un problème de portée. (Ajouté à la modification: en fait, la réponse de sris traite de ce point à la fin, mais je laisserai cette réponse de toute façon, car je pense que l'exemple de code pourrait être utile pour comprendre le problème.)

Dans une classe Ruby, un nom de variable commençant par @ peut faire référence à l'une des deux variables: soit à une variable d'instance , soit à une variable d'instance de classe , en fonction de l'endroit où il est fait référence dans la classe. Ceci est un gotcha assez subtil.

Un exemple clarifiera le point. Voici une petite classe de test Ruby (tout le code testé en irb):

class T

  @@class_variable = "BBQ"
  @class_instance_variable_1 = "WTF"
  @class_instance_variable_2 = "LOL"

  def self.class_method
    puts "@@class_variable           == #{@@class_variable           || 'nil'}"
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
    puts "@instance_variable         == #{@instance_variable         || 'nil'}"
  end

  def initialize
    @instance_variable = "omg"
    # The following line does not assign a value to the class instance variable,
    # but actually declares an instance variable withthe same name!
    @class_instance_variable_1 = "wtf"
    puts "@@class_variable           == #{@@class_variable           || 'nil'}"
    # The following two lines do not refer to the class instance variables,
    # but to the instance variables with the same names.
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
    puts "@instance_variable         == #{@instance_variable         || 'nil'}"
  end

  def instance_method
    puts "@@class_variable           == #{@@class_variable           || 'nil'}"
    # The following two lines do not refer to the class instance variables,
    # but to the instance variables with the same names.
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
    puts "@instance_variable         == #{@instance_variable         || 'nil'}"
  end

end

J'ai nommé les variables en fonction de ce que je pensais être, bien que cela ne soit pas toujours le cas:

irb> T.class_method
@@class_variable           == BBQ
@class_instance_variable_1 == WTF    # the value of the class instance variable
@class_instance_variable_2 == LOL    # the value of the class instance variable
@instance_variable         == nil    # does not exist in the class scope
=> nil

irb> t = T.new
@@class_variable           == BBQ
@class_instance_variable_1 == wtf    # the value of the instance variable
@class_instance_variable_2 == nil    # the value of the instance variable
@instance_variable         == omg
=> #<T:0x000000015059f0 @instance_variable="omg", @class_instance_variable_1="wtf">

irb> t.instance_method
@@class_variable           == BBQ
@class_instance_variable_1 == wtf    # the value of the instance variable
@class_instance_variable_2 == nil    # the value of the instance variable
@instance_variable         == omg
=> nil

irb> T.class_method
@@class_variable           == BBQ
@class_instance_variable_1 == WTF    # the value of the class instance variable
@class_instance_variable_2 == LOL    # the value of the class instance variable
@instance_variable         == nil    # does not exist in the class scope
=> nil

La @@ variable_de_classe et le variable_instance_ / <> code> se comportent toujours comme prévu: le premier est défini au niveau de la classe, et qu'il soit référencé dans une méthode de classe ou dans une méthode d'instance, il contient la valeur qui lui est assignée en haut. Ce dernier n'obtient qu'une valeur dans un objet de classe T , donc dans une méthode de classe, il fait référence à une variable inconnue dont la valeur est nil .

La méthode class nommée de façon imaginative class_method génère les valeurs de @@ variable_source et des deux @class_instance_variable , comme prévu, c'est-à-dire initialisé en haut de la classe. Cependant, dans les méthodes d'instance initialize et méthode_instance , différentes variables du même nom sont utilisées, c'est-à-dire variables d'instance, pas de variables d'instance de classe .

Vous pouvez constater que l'affectation dans la méthode initialize n'a pas affecté la variable d'instance de classe @ class_instance_variable_1 , car le dernier appel de class_method . renvoie son ancienne valeur, " WTF " . Au lieu de cela, la méthode initialize a déclaré une nouvelle variable d'instance, qui est également nommée (de manière erronée) @ class_instance_variable_1 . La valeur qui lui est assignée, " wtf ", est sortie par les méthodes initialize et méthode_instance .

La variable @ class_instance_variable_2 de l'exemple de code est équivalente à la variable @hello du problème d'origine: elle est déclarée et initialisée en tant que variable d'instance de classe, mais lorsqu'il s'agit d'une instance. méthode fait référence à une variable de ce nom; elle voit en effet une variable d'instance portant le même nom - une variable qui n'a jamais été déclarée; sa valeur est donc nil.

Je vous recommanderais également d'examiner les variables de classe ayant le préfixe " @@ " - voici un exemple de code pour vous montrer en quoi les vars de classe et d'instance sont différents:

class Vars
  @@classvar="foo"
  def test
    @instancevar="bar"
  end
  def Vars.show
    puts "classvar: #{@@classvar}"
    puts "instancevar: #{@instancevar}"
  end
  def instance_show
    puts "classvar: #{@@classvar}"
    puts "instancevar: #{@instancevar}"

  end
end

# only shows classvar since we don't have an instance created
Vars::show
# create a class instance
vars = Vars.new
# instancevar still doesn't show b/c it hasn't been initialized
vars.instance_show
# initialize instancevar
vars.test
# now instancevar shows up as we expect
vars.instance_show
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top