2009-05-05 11 views
61
class Hello 
@hello = "hello" 
    def display 
     puts @hello 
    end 
end 

h = Hello.new 
h.display 

Ich erstellte die oben genannte Klasse. Es wird nichts ausgedruckt. Ich dachte, dass die Instanzvariable @hello während der Klassendeklaration gesetzt wurde. Aber wenn ich die Anzeigemethode anrufe, ist die Ausgabe 'Null'. Was ist der richtige Weg, dies zu tun?Wann werden Ruby-Instanzvariablen gesetzt?

Antwort

88

Instanzvariablen in Ruby ein wenig verwirrend sein kann, wenn die ersten Ruby-Lernen, vor allem wenn man wie Java auf einem anderen OO-Sprache gewöhnt sind .

Sie können eine Instanzvariable nicht einfach deklarieren.

Eines der wichtigsten Dinge zu wissen über Instanzvariablen in Ruby, abgesehen von der Notation mit einem @ -Zeichen-Präfix, ist, dass sie zum ersten Mal ins Leben kommen, wenn sie zugeordnet sind.

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

können Sie die Methode verwenden Object#instance_variables alle Instanzvariablen eines Objekts aufzulisten.

Normalerweise deklarieren und initialisieren Sie alle Instanzvariablen in der Initialisierungsmethode. Eine weitere Möglichkeit, eindeutig zu dokumentieren, welche Instanzvariablen öffentlich verfügbar sein sollen, ist die Verwendung der Modulmethoden attr_accessor (lesen/schreiben), attr_writer (schreiben) und attr_reader (lesen). Diese Methoden synthetisieren verschiedene Zugriffsmethoden für die aufgelistete Instanzvariable.

class Hello 
    attr_accessor :hello 
end 

h = Hello.new 
p h.instance_variables 

h.hello = "hello" 
p h.instance_variables 

# Output 
[] 
["@hello"] 

Die Instanzvariable noch nicht erstellt wird, bis sie zur Verwendung der synthetisierten Hello#hello= Methode zugewiesen wird.

Ein anderes wichtiges Problem, wie kch beschrieben, ist, dass Sie sich der verschiedenen Kontexte bewusst sein müssen, die beim Deklarieren einer Klasse aktiv sind. Beim Deklarieren einer Klasse wird der Standardempfänger (self) im äußersten Bereich das Objekt sein, das die Klasse selbst darstellt. Daher wird Ihr Code zuerst eine Klasseninstanzvariable erstellen, wenn Sie auf Klassenebene @hello zuweisen.

Innere Methoden Selbst wird das Objekt, auf dem sein die Methode aufgerufen wird, also versuchen Sie, den Wert einer Instanzvariable mit dem Namen @hello in dem Objekt zu drucken, die nicht (beachten Sie tut besteht, dass es perfekt ist legal, um eine nicht vorhandene Instanzvariable zu lesen).

+2

Du sagst "sie kommen zum ersten Mal ins Leben, wenn sie ihnen zugewiesen werden", aber das OP zeigte ein Beispiel mit (scheinbarer) Zuweisung früher als in deinem Beispiel, und das Problem ist, dass diese Variable nicht zum Leben geführt hat. – kaleidic

+1

@ kaleidic Genau. Ich bin ein wenig verwirrt über die Anzahl der Upvotes zu dieser Antwort, die nicht auf die Frage des OP eingehen. Tatsächlich tritt die Klasseninstanzvariable '@ hello' * * in Zeile 2 des Beispielcodes auf, aber das Problem ist, dass dies nicht die Variable ist, auf die sich Zeile 4 bezieht. Siehe meine Antwort unten für weitere Details. –

+0

Entschuldigung, Sie beantworten am Ende tatsächlich die Frage, die ich bei der ersten Lesung übersehen habe. –

42

Sie benötigen eine initialize Methode hinzufügen:

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

h = Hello.new 
h.display 
21

Die erste @hello in Ihrem Code wird eine Instanz der Klasse Variable genannt.

Es ist eine Instanzvariable des Klassenobjekts, auf die die Konstante Hello zeigt. (Und das ist eine Instanz der Klasse Class.)

Technisch gesehen, wenn Sie im class Umfang sind, Ihre self wird auf das Objekt Ihrer aktuellen Klasse und @variables beziehen sich auf Ihren aktuellen self. Junge, ich sauge daran, diese Dinge zu erklären.

Sie können all dies und viel mehr geklärt werden, indem Sie this collection of $5-each screencasts from The Pragmatic Programmers beobachten.

(Sie können auch hier für Klärung bitten, und ich werde zu aktualisieren versuchen.)

+0

Eine gute [article] (http://www.railtips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/) Ausarbeitung auf Instanzvariablen auf Klassenebene. –

9

gibt es eine klare Beschreibung in dem Buch "The Ruby Programmiersprache", lesen Sie es wird sehr hilfreich sein. I Paste hier (aus Kapitel 7.1.16):

eine Instanzvariable innerhalb einer Klassendefinition verwendet, aber außerhalb einer Instanzmethode Definition ist ein Klasseninstanzvariable.

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 

......

Weil Klasse Instanzvariablen nur Instanzvariablen der Klasse Objekte sind, können wir attr, attr_reader und attr_accessor verwenden Accessormethoden für sie zu schaffen .

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

Mit diesen Accessoren definiert ist, können wir unsere Rohdaten als Point.n, Point.totalX und Point.totalY beziehen.

1

ich auch empfehlen würde bei Klassenvariablen suchen, die mit dem Präfix „@@“ - hier einige Beispiel-Code, den Sie zu zeigen, wie Klassen- und Instanz Vars unterschiedlich sind:

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 
4

ich hatte vergessen, es war ein "Klasseninstanzvariable" -Konzept in Ruby. Auf jeden Fall schien das Problem des OP rätselhaft zu sein und wurde in keiner der Antworten bis jetzt behandelt, abgesehen von einem Hinweis in kchs Antwort: Es ist ein Problem des Umfangs. (Hinzugefügt am Edit: Eigentlich sris Antwort tut adressieren diesen Punkt am Ende, aber ich werde diese Antwort trotzdem stehen lassen, da ich denke, dass der Beispielcode zum Verständnis des Problems nützlich sein könnte.)

In einem Ruby-Klasse, ein Variablenname beginnend mit @ kann sich auf eine zwei Variablen beziehen: entweder auf eine Instanzvariable oder auf eine Klasseninstanzvariable, je nachdem, wo in der Klasse, auf die sie bezogen ist. Das ist ein ziemlich subtiler Fehler.

Ein Beispiel wird den Punkt verdeutlichen. Hier ist eine kleine Ruby-Test-Klasse (alle Code in irb getestet):

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 

Ich nannte die Variablen nach dem, was ich dachte, sie waren, aber das erweist sich nicht immer der Fall zu sein:

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 

Die @@class_variable und @instance_variable verhalten sich immer wie erwartet: ersteres ist auf der Klassenebene definiert, und ob es in einer Klassenmethode oder einer Instanzmethode referenziert wird, es enthält Wert, der ihm oben zugeordnet ist. Letzterer erhält nur einen Wert in einem Objekt der Klasse T. In einer Klassenmethode verweist er auf eine unbekannte Variable, deren Wert nil ist.

Die Klassenmethode imaginativ mit dem Namen class_method gibt die Werte @@class_variable und die beiden @class_instance_variable s wie erwartet aus, dh wie oben in der Klasse initialisiert. Jedoch in den Instanzmethoden initialize und instance_method, verschiedenen Variablendes gleichen Namens zugegriffen wird, das heißt, Instanzvariablen, nicht die Klasseninstanzvariablen.

können Sie sehen, dass die Zuordnung in der initialize Methode nicht die Klasseninstanzvariable @class_instance_variable_1, weil der spätere Aufruf von class_method gibt seinen alten Wert nicht beeinträchtigte, "WTF". Stattdessen hat Methode initializeeine neue Instanzvariable deklariert, eine, die ist auch benannt (irreführend) @class_instance_variable_1. Der Wert zugewiesen, "wtf", ausgegeben durch Verfahren initialize und instance_method.

Die Variable @class_instance_variable_2 im Beispiel Code variabler @hello in das ursprüngliche Problem äquivalent: es als Klasseninstanzvariable initialisiert erklärt und wird, aber, wenn eine Instanz-Methode auf eine Variable dieses Namens bezieht, sieht es tatsächlich eine Instanz Variable mit dem gleichen Namen - eine, die nie erklärt wurde, so ist ihr Wert Null.

+0

nur diese Antwort macht Sinn :) – InQusitive

+0

@InQuitive froh, um Hilfe zu sein! –

Verwandte Themen