2014-02-20 40 views
5

eine Klassenvariable @@foo in zwei Klassen einstellen B und C, wo weder eine Unterklasse der anderen ist, aber sie beide einen gemeinsamen Modul A, @@foo separat für B und C zu schaffen scheint, die A nicht zugreifen kann :Umfang der Klassenvariable

module A; end 
class B; include A; @@foo = 1 end 
class C; include A; @@foo = 2 end 

module A; p @@foo end # => NameError: uninitialized class variable @@foo in A 
class B; p @@foo end # => 1 
class C; p @@foo end # => 2 

Aber wenn @@foo in A zugeordnet ist, das funktioniert sowohl als ein Vorfahr B und C, die @@foo dass B und C Access wird der @@foo von A.

module A; @@foo = 3 end 
class B; p @@foo end # => 3 
class C; p @@foo end # => 3 

Was geschah mit dem @@foo von B und C? Werden sie gelöscht, wenn einer ihrer Vorfahren @@foo zugewiesen ist?

+0

@ArupRakshit Das ist nicht die Antwort. Eigentlich kam meine Frage aus dieser Antwort. Ihre Antwort ist der Ausgangspunkt dieser Frage. – sawa

+0

Ich habe das .. Lass mich nachdenken .. :-) Gute Frage. –

Antwort

2

Dieser Code sowohl in rb_cvar_set und rb_cvar_get in MRI erscheint variable.c:

if (front && target != front) { 
    st_data_t did = id; 

    if (RTEST(ruby_verbose)) { 
     rb_warning("class variable %"PRIsVALUE" of %"PRIsVALUE" is overtaken by %"PRIsVALUE"", 
      QUOTE_ID(id), rb_class_name(original_module(front)), 
      rb_class_name(original_module(target))); 
    } 
    if (BUILTIN_TYPE(front) == T_CLASS) { 
     st_delete(RCLASS_IV_TBL(front),&did,0); 
    } 
} 

id ist die C-interne Darstellung des Variablennamen (@@foo).

front ist die Klasse, in der die Variable derzeit zugegriffen wird (B/C).

target ist der fernsten Vorfahr in dem die Variable hat auch je (A) definiert.

Wenn front und target sind nicht die gleichen, warnt Rubin, dass class variable #{id} of #{front} is overtaken by #{target}.

Der Variablenname wird dann gelöscht buchstäblich von front ‚s RCLASS_IV_TBL, so dass bei nachfolgenden Abfragen, die Suche nach dem Variablennamen ‚fällt durch‘ oder ‚sprudelt‘ zu den am weitesten entfernten Vorfahren in welche die Variable definiert ist.


Beachten Sie, dass diese Prüfung und Löschung passieren nicht nur auf Cvar bekommt, sondern setzt auch:

$VERBOSE = true 

module A; end 
class B; include A; @@foo = 1; end # => 1 

module A; @@foo = 3 end # => 3 
class B; p @@foo = 1 end # => 1 
#=> warning: class variable @@foo of B is overtaken by A 


module A; p @@foo end # => 1 

In diesem Beispiel, obwohl esA‚s-Wert vonwird überschrieben durch den Wert 1 wird in B gesetzt, wir noch rec eiive die gleiche Warnung, dass es B 's Klassenvariable wird von A überholt!

Während es für den durchschnittlichen Ruby-Programmierer normalerweise überraschender ist zu finden, dass sich der Wert ihrer Variablen an verschiedenen, vielleicht unerwarteten Stellen ändert (zB in "Eltern"/"Großeltern"/"Onkel"/"Cousine")/"Schwester" -Module und -Klassen), der Auslöser und der Wortlaut zeigen beide an, dass die Warnung eigentlich den Codierer informieren soll, dass sich die Variable der "Quelle der Wahrheit" der Variablen geändert hat.

+1

Danke für die gute Antwort. Das Ergebnis "1" für "A" in deiner letzten Zeile war überraschend für mich. Die Existenz der Zeile mit der Zuweisung von "3" in "A" macht einen Unterschied zu dieser letzten Zeile. Um es so umzuformulieren, scheint es so zu sein, dass (1) Setzen und Erhalten durch die Vorfahrenhierarchie nach oben oder nach unten geleitet werden kann, aber (2) nur innerhalb der Domäne, in der die Variable existiert, und (3) die Existenz kann nur sein nach unten geerbt, und (4) nicht oberste Variablen in der Hierarchie werden gelöscht. – sawa

+0

Sie sind fantastisch +1 –

1

Meine folgenden Notizen stammen aus Metaprogramming Ruby (by Paolo Perrotta), die ich zufällig gerade gelesen habe, als ich auf Ihre Frage stieß. Ich hoffe, dass diese Auszüge (Seitenzahlen in Klammern) und meine Erklärung hilfreich für Sie sind.

Beachten Sie, dass Klassenvariablen unterscheiden sich von Klasse Instanzvariablen.

A-Klasse Instanzvariable gehört zu einem Objekt der Klasse Class und ist zugänglich nur durch die Klasse selbst - nicht durch eine Instanz oder durch eine Unterklasse. (106)

Die Klassenvariable, auf der anderen Seite gehört zur Klasse Hierarchien. Das bedeutet, dass es zu jeder Klasse sowie allen Nachkommen dieser Klasse gehört. Hier

ist ein Beispiel des Autors:

@@v = 1 

class MyClass 
    @@v = 2 
end 

@@v # => 2 

Sie dieses Ergebnis erhalten, weil Klassenvariablen wirklich nicht zu Klassen gehören - sie gehören zu der Klasse Hierarchien. Da @@ v in im Kontext von main definiert ist, gehört es zu main's Klasse Object ... und zu alle Nachkommen von Object. MyClass erbt von Object, also es teilt sich die gleiche Klassenvariable. (107)

Aber auch, da Ihre spezielle Frage hat mit Klassen nicht zu tun, nur, sondern auch mit den Modulen:

Wenn Sie ein Modul in einer Klasse gehören, Ruby eine anonyme Klasse erstellen Dadurch wird das Modul umbrochen und die anonyme Klasse in die -Kette oberhalb der einschließenden Klasse eingefügt.(26)

So, wie Sie bei B.ancestors anschauen, werden Sie sehen:

=> [B, A, Object, Kernel, BasicObject] 

Auch für C.ancestors, werden Sie sehen:

=> [C, A, Object, Kernel, BasicObject] 

Wenn wir bedenken, dass Klassenvariablen gehören zu Klassenhierarchien, dann die Klassenvariable @@foo, sobald sie in Module A definiert ist (und so die anonyme Klasse knapp über B tha t ist erstellt, sobald B enthält A), gehört zu B (und auch zu C, da es A enthält).

Um es einfach auszudrücken:

  1. Wenn @@foo nur in B und (aber nicht in A) in C definiert wurde, dann B hatte eine Klassenvariable @@foo, die anders als die Klassenvariable @@foo in C war. Dies liegt daran, dass die Klassenvariablen nur für diese Klasse und für alle Nachkommen zugänglich sind. Aber B und C sind durch ihre Vorfahren A und nicht durch ihre Nachkommen verwandt.
  2. Sobald @@foo in A definiert wurde, wurde diese Klassenvariable von allen Nachkommen von A geerbt - also B und C. Von nun an verweist die Referenz auf @@foo in Klasse B tatsächlich auf die Klassenvariable, die zu A gehört. Das Original @@foo, das in B definiert wurde, wurde überschrieben ersetzt (übernommen von seinem Vorfahren). Dasselbe ist mit der @@foo in passiert. B und C können sowohl in dieselbe Klassenvariable @@foo schreiben als auch von ihr lesen, da sie zu ihrem gemeinsamen Vorfahren A gehört.

An dieser Stelle jemand von A, B oder C können alle @@foo ändern. Zum Beispiel:

class B 
    p @@foo # => 3 
    @@foo = 1 
end 

module A 
    p @@foo # => 1 
end 
+0

In der Antwort Teil von dem, was Sie geschrieben haben, ist: 'Das Original @@ foo, die in B definiert wurde, wurde überschrieben (übernommen von seinem Vorfahren). Ich glaube nicht, dass der Ausdruck "überschreiben" korrekt ist. Es impliziert, dass es noch eine solche Variable gibt. – sawa

+0

Das ist wahr. * Überschreiben * ist möglicherweise nicht das beste Wort. Vielleicht ist * ersetzt * ein besseres Wort - ersetzt durch eine völlig andere Variable, die zu einer anderen (Vorfahr-) Klasse gehört, aber mit demselben Namen. –