2014-02-20 2 views
6

Предположит простую программу рубиновой, используя переменный класс,Путаница класса рубинового переменной

class Holder 
     @@var = 99 
     def Holder.var=(val) 
     @@var = val 
     end 
     def var 
     @@var 
     end 
    end 

    @@var = "top level variable" 

    a = Holder.new 
    puts a.var 

Я думаю, результат должен быть 99, но выход не 99. Интересно, почему. Поскольку область переменной класса является классом, я предполагаю, что строка @@var = "top level variable" не повлияет на переменную в классе.

ответ

6

@@var - переменная класса Holder. И @@var на верхнем уровне - это не та же переменная класса с таким же именем @@var из Holder, это вы создаете совершенно новую переменную класса для класса Object. Теперь @@var используется совместно с подклассами родительского класса. Object является родительским классом класса Holder. В Ruby, если вы явно не определяете суперкласс любого настраиваемого класса, вы определяете с помощью ключевого слова class, тогда класс Object становится суперклассом по умолчанию класса, который вы только что создали.

На верхнем уровне вы определяете новую переменную класса в классе Object, как и вы, в вашем опубликованном примере. Как и через наследование, он теперь доступен классу Holder.

Я имею в виду, когда вы писали ниже:

class Holder 
    @@var = 99 
    def Holder.var=(val) 
    @@var = val 
    end 
    def var 
    @@var 
    end 
end 

@@var не добавляется уже к Object класса. Теперь на верхнем уровне, когда вы написали строку ниже:

@@var = "top level variable" 

Это означает, что вы добавляете его в Object класса, а также обновляет старые переменной (@@var) такое же имя, как и в Holder классе один, с этим новым один, вы только что определили в классе Object.

Помните, переменные класса разделяемые переменные и видимы только к классу (B), где вы определили его и его потомок класса (C), потомок (D) класса потомка (C), так далее. Но не видно суперклассу (A, как показано ниже) класса (B, как показано ниже), до тех пор, пока вы не определили одну и ту же переменную имени в родительском классе (A), как у вас в дочернем классе (B) ,

class A 
end 

class B < A 
    @@var = 10 
end 

class C < B 
end 

class D < C 
end 

A.class_variable_get(:@@var) # uninitialized class variable @@var in A (NameError) 
B.class_variable_get(:@@var) # 10 
C.class_variable_get(:@@var) # 10 
D.class_variable_get(:@@var) # 10 
1

Я хотел бы, чтобы усилить то, что другие сказали, и, в частности, сравнить использование переменных класса с экземпляра класса переменных. Давайте начнем с этого:

class Holder1 
end 

class Holder2 < Holder1 
    @@var = 99 

    # Create a class setter and a getter for the class variable 
    def Holder2.var=(val) 
    @@var = val 
    end 

    def Holder2.var 
    @@var 
    end 

    # Create a instance getter for the class variable 
    def var 
    @@var 
    end 
end 

class Holder3 < Holder2 
end 

Holder2.var  #=> 99 
Holder2.var = 1 
Holder3.var  #=> 1 
Holder1.var  #=> NoMethodError: undefined method `var' for Holder1:Class 
Holder2.new.var #=> 1 
Holder3.new.var #=> 1 

Holder3.var = 2 
Holder3.var  #=> 2 
Holder2.var  #=> 2 
Holder3.new.var #=> 2 
Holder2.new.var #=> 2 

Помимо: первые два метода, как правило, записывается следующим образом:

def self.var=(val) 
    @@var = val 
    end 

    def self.var 
    @@var 
    end 

Это работает, потому что self =>Holder2 когда методы создания. Не устанавливая имя класса, никаких изменений не требуется, если вы решите переименовать класс.

Теперь это (использование переменной класса) может быть именно тем поведением, которое вы хотите.То есть, если вы хотите, чтобы подклассы видели переменную и могли ее изменить, переменная класса - это то, что вам нужно.

Если, однако, вы хотите, чтобы каждый подкласс иметь свою собственную переменную, которая не может быть ни видно, ни изменен подклассов, вы должны использовать экземпляр класса переменной, @var, а не переменную класса, @@var. (Технически это не совсем правильно, потому что можно было использовать Holder2.instance_variable_get(:@var) или Holder2.instance_variable_set(:@var) в любой точке вашей программы.)

Сравните результаты кода, приведенного выше. Я включил переменную экземпляра с тем же именем, что и переменная экземпляра класса, @var, чтобы показать, что они такие же разные, как и @night и @day.

class Holder1 
end 

class Holder2 < Holder1 
    # Create an accessor for the instance variable 
    attr_accessor :var 

    # Initialize class instance variable 
    @var = 99 

    # Create an accessor for the class instance variable 
    class << self 
    puts "self in 'class << self': #{self}" 
    attr_accessor :var 
    def our_dog 
     "Diva" 
    end 
    end 

    # Create a class method 
    def self.our_cat 
    puts "self in 'self.our_cat())' def: #{self}" 
    "Teagan" 
    end 

    # Create an instance setter and a getter for the class instance variable 
    def c_ivar=(val) 
    self.class.var = val 
    end 

    def c_ivar 
    self.class.var 
    end 
end 

class Holder3 < Holder2 
end 

       #=> self in 'class << self': #<Class:Holder2> 

Holder2.var  #=> 99 
h2 = Holder2.new 
h2.var   #=> nil 

Holder2.var = 1 
Holder2.var  #=> 1 
h2.c_ivar  #=> 1 
h2.c_ivar = 2 
Holder2.var  #=> 2 

h2.var   #=> nil 
h2.var = 3 
h2.var   #=> 3 
Holder2.var  #=> 2 

Holder3.var  #=> nil 
Holder1.var  #=> NoMethodError: undefined method `var' for Holder1:Class 

Holder3.var = 4 
Holder3.var  #=> 4 
Holder2.var  #=> 2 
h3 = Holder3.new 
h3.c_ivar  #=> 4 
h2.c_ivar  #=> 2 

Holder3.our_dog #=> "Diva" 
Holder3.our_cat #=> "self in 'self.our_cat())' def: Holder3" 
       #=> "Teagan" 

Holder1.var вызывает исключение, потому что класс не имеет доступа к экземпляру класса Holder2 «s переменной [email protected]. Напротив, первое использование Holder3.var возвращает nil, потому что переменная существует для Holder3 по наследству, но не была инициализирована.

Выражение class << self не меняет self от Holder2 для одноплодного класса Holder2 «s (ака метакласса) до следующего end. Обратите внимание, что Ruby обозначает одноэлементный класс Holder2 как #<Class:Holder2>. Кроме того, нам не нужно добавлять my_dog с self., так как self - это одноэлементный класс Holder2 при создании метода.

Обратите внимание, что:

Holder2.singleton_methods #=> [:var, :var=, :our_dog, :our_cat] 

Это показывает, что our_cat() является методом Holder2 «s одноплодного класса, несмотря на то, self был Holder2 (а не Holder2 's одноэлементного класса', #<Class:Holder2>), когда метод был построен , По этой причине некоторые говорят, что технически "there is no such thing as a class method". Это также говорит нам о том, что мы могли бы переместить определение my_cat в конструкцию class << self (и упали self.).

Другой распространенный способ добавить переменные экземпляра и методы Holder2 «одноплодной класса s является замена class << self ... end с extend M и создать модуль:

module M 
    attr_accessor :var 
    def our_dog 
    "Diva" 
    end 
end 

Object#extend смешивает эти переменные экземпляра и методы в Holder2» s одноплодной класса ,

Смежные вопросы