66 votes

Quand les variables d'instance de Ruby sont-elles définies ?

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

h = Hello.new
h.display

J'ai créé la classe ci-dessus. Elle n'imprime rien. Je pensais que la variable d'instance @hello avait été définie lors de la déclaration de la classe. Mais lorsque j'appelle la méthode d'affichage, la sortie est 'nil'. Quelle est la manière correcte de procéder ?

95voto

sris Points 3040

Les variables d'instance en Ruby peuvent être un peu déroutantes lors de l'apprentissage de Ruby, surtout si vous êtes habitué à un autre langage OO comme Java.

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

Une des choses les plus importantes à savoir sur les variables d'instance en ruby, en dehors de la notation avec un préfixe de signe @, est que ils prennent vie la première fois qu'ils sont assignés à .

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 Object#instance_variables pour lister toutes les variables d'instance d'un objet.

Normalement, vous "déclarez" et initialisez toutes les variables d'instance dans la méthode initialize. Une autre façon de documenter clairement les variables d'instance qui doivent être accessibles au public est d'utiliser les méthodes du module attr_accessor (lecture/écriture), attr_writer (écrire) et attr_reader (lire). Ces méthodes synthétiseront différentes méthodes d'accesseur pour la variable d'instance listé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 assignée à l'aide de la fonction synthétisée Hello#hello= méthode.

Un autre problème important, comme l'a décrit 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 contexte récepteur par défaut (self) dans la portée 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.

Méthodes internes self sera l'objet sur lequel la méthode est invoquée, donc vous essayez d'imprimer la valeur d'une variable d'instance portant le nom de @hello dans l'objet, qui n'existe pas (notez qu'il est parfaitement légal de lire une variable d'instance inexistante).

3 votes

Vous dites "elles prennent vie la première fois qu'elles sont assignées à" alors que le PO a montré un exemple avec une assignation (apparente) plus tôt que dans votre exemple, et le problème rencontré est que ladite variable n'avait pas pris vie.

1 votes

@kaleidic Exactement. Je suis un peu perplexe devant le nombre de upvotes sur cette réponse qui ne répond pas à la question de l'OP. En fait, la variable d'instance de la classe @hello hace prend vie à la ligne 2 du code d'exemple, mais le problème est que ce n'est pas la variable à laquelle la ligne 4 fait référence. Voir ma réponse ci-dessous pour plus de détails.

0 votes

Désolé, vous répondez effectivement à la question à la fin, ce que j'ai réussi à manquer lors de la première lecture.

44voto

Nathan Kitchen Points 2729

Vous devez ajouter un initialize méthode :

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

h = Hello.new
h.display

24voto

kch Points 25855

Le premier @hello dans votre code est appelée une variable d'instance de classe.

Il s'agit d'une variable d'instance de la classe objet que la constante Hello pointe vers. (et qui est une instance de la classe Class .)

Techniquement, quand vous êtes dans le class portée, votre self est défini comme l'objet de votre classe actuelle, et @variables se rapportent à votre self . Je suis nul pour expliquer ces choses.

Vous pouvez obtenir toutes ces informations et bien d'autres encore en regardant le site suivant cette collection de screencasts à 5 $ chacun de The Pragmatic Programmers. .

(Ou vous pouvez demander des clarifications ici et j'essaierai de mettre à jour).

0 votes

Un bon article en élaborant sur les variables d'instance de niveau classe.

10voto

lfx_cool Points 937

Il y a une description claire dans le livre "The ruby programming language", le lire sera très utile. Je la colle ici (du chapitre 7.1.16) :

Une variable d'instance utilisée à l'intérieur d'une définition de classe mais en dehors d'une méthode d'instance. d'une 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 d'objets de classe. nous pouvons utiliser attr, attr_reader, et attr_accessor pour créer des méthodes pour leur créer des méthodes d'accès.

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

Avec ces accesseurs définis, nous pouvons nous référer à nos données brutes en tant que Point.n, Point.totalX, et Point.totalY.

1voto

Steve Midgley Points 435

Je vous recommande également de regarder les variables de classe qui sont préfixées par "@@" - voici un exemple de code pour vous montrer comment les variables de classe et d'instance sont différentes :

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

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