Domanda

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

h = Hello.new
h.display

Ho creato la classe sopra. Non stampa nulla. Pensavo che la variabile di istanza @hello fosse stata impostata durante la dichiarazione di classe. Ma quando chiamo il metodo di visualizzazione, l'output è "zero". Qual è il modo corretto per farlo?

È stato utile?

Soluzione

Le variabili di istanza in ruby ??possono essere un po 'confuse quando si impara per la prima volta Ruby, specialmente se si è abituati ad un'altra lingua OO come Java.

Non puoi semplicemente dichiarare una variabile di istanza.

Una delle cose più importanti da sapere sulle variabili di istanza in ruby, oltre alla notazione con un prefisso @, è che prendono vita la prima volta che vengono assegnati a .

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"]

Puoi usare il metodo Oggetto # istanza_istanze per elencare tutte le variabili di istanza di un oggetto.

Normalmente & # 8220; dichiari & # 8221; e inizializza tutte le variabili di istanza nel metodo di inizializzazione. Un altro modo per documentare chiaramente quali variabili di istanza dovrebbero essere disponibili pubblicamente è utilizzare i metodi del modulo attr_accessor (lettura / scrittura), attr_writer (scrittura) e attr_reader (leggi). Questi metodi sintetizzeranno diversi metodi di accesso per la variabile di istanza elencata.

class Hello
  attr_accessor :hello
end

h = Hello.new
p h.instance_variables 

h.hello = "hello"
p h.instance_variables

# Output
[]
["@hello"]

La variabile di istanza non è ancora stata creata fino a quando non viene assegnata all'uso del metodo sintetizzato Hello # hello = .

Un altro problema importante, come descritto da kch, è che devi essere consapevole dei diversi contesti attivi quando dichiari una classe. Quando si dichiara una classe il ricevitore predefinito (self) nell'ambito più esterno sarà l'oggetto che rappresenta la classe stessa. Quindi il tuo codice creerà prima una variabile di istanza di classe quando assegni a @hello a livello di classe.

I metodi interni self saranno l'oggetto su cui viene invocato il metodo, quindi stai provando a stampare il valore di una variabile di istanza con il nome @hello nel oggetto, che non esiste (nota che è perfettamente legale leggere una variabile di istanza inesistente).

Altri suggerimenti

Devi aggiungere un metodo inizializza :

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

h = Hello.new
h.display

Il primo @hello nel tuo codice è chiamato variabile di istanza di classe.

È una variabile di istanza dell'oggetto classe a cui punta la costante Hello . (e che è un'istanza della classe Class .)

Tecnicamente, quando ti trovi nell'ambito class , il tuo self è impostato sull'oggetto della tua classe corrente e @variables pertinente al tuo attuale self . Ragazzo, faccio schifo a spiegare queste cose.

Puoi ottenere tutto questo e molto di più chiarendoti guardando questa raccolta di $ 5 ciascuno degli screencast di The Pragmatic Programmers .

(O puoi chiedere chiarimenti qui e proverò ad aggiornare.)

c'è una chiara descrizione nel libro "Il linguaggio di programmazione ruby", leggilo sarà molto utile. Lo incollo qui (dal capitolo 7.1.16):

  

Una variabile di istanza utilizzata all'interno di una definizione di classe ma all'esterno di una   la definizione del metodo di istanza è una variabile di istanza di 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

......

  

Perché le variabili di istanza di classe sono solo variabili di istanza di classe   oggetti, possiamo usare attr, attr_reader e attr_accessor per creare   metodi di accesso per loro.

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

Con questi accessori definiti, possiamo fare riferimento ai nostri dati grezzi come Point.n, Point.totalX e Point.totalY.

Avevo dimenticato che c'era una "variabile di istanza di classe" concetto in rubino. In ogni caso, il problema del PO sembrava sconcertante, e non è stato realmente affrontato in nessuna delle risposte fino ad ora, tranne per un suggerimento nella risposta di Kch: è un problema di portata. (Aggiunto in modifica: in realtà, la risposta di sris risolve questo punto alla fine, ma lascerò comunque questa risposta, poiché penso che il codice di esempio possa essere utile per comprendere il problema.)

In una classe Ruby, un nome di variabile che inizia con @ può fare riferimento a una delle due variabili: a variabile di istanza o a una variabile di istanza di classe , a seconda di dove nella classe viene indicato. Questo è un gotcha abbastanza sottile.

Un esempio chiarirà il punto. Ecco una piccola classe di test di Ruby (tutto il codice testato in 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

Ho chiamato le variabili in base a ciò che pensavo fossero, anche se questo non è sempre il caso:

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

@@ class_variable e @instance_variable si comportano sempre come ci si aspetterebbe: il primo è definito a livello di classe e se indicato in un metodo di classe o in un metodo di istanza, contiene il valore assegnato ad esso in alto. Quest'ultimo ottiene solo un valore in un oggetto della classe T , quindi in un metodo di classe fa riferimento a una variabile sconosciuta il cui valore è nil .

Il metodo di classe fantasiosamente chiamato class_method produce i valori di @@ class_variable e i due @class_instance_variable come previsto, ovvero come inizializzato al vertice della classe. Tuttavia, nei metodi di istanza inizializza e instance_method , diverse variabili con lo stesso nome , ovvero, variabili di istanza, non variabili di istanza di classe .

Puoi vedere che l'assegnazione nel metodo inizializza non ha influenzato la variabile dell'istanza di classe @ class_instance_variable_1 , perché la chiamata successiva di class_method produce il suo vecchio valore, " WTF " . Invece, il metodo inizializza ha dichiarato una nuova variabile di istanza, una che è anche nominata (in modo fuorviante) @ class_instance_variable_1 . Il valore assegnato ad esso, " wtf " , viene emesso dai metodi inizializza e esempio_istanza .

La variabile @ class_instance_variable_2 nel codice di esempio è equivalente alla variabile @hello nel problema originale: è dichiarata e inizializzata come variabile di istanza di classe, ma quando un'istanza Il metodo fa riferimento a una variabile con quel nome, in realtà vede una variabile di istanza con lo stesso nome - una che non è mai stata dichiarata, quindi il suo valore è zero.

Consiglierei anche di esaminare le variabili di classe con il prefisso " @@ " - ecco un po 'di codice di esempio per mostrarti come le differenze tra classe e istanza sono diverse:

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top