When do Ruby instance variables get set?

前端 未结 6 1677
盖世英雄少女心
盖世英雄少女心 2020-12-02 04:42
class Hello
@hello = \"hello\"
    def display
        puts @hello
    end
end

h = Hello.new
h.display

I created the class above. It doesn\'t prin

6条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-02 05:24

    I'd forgotten that there was a "class instance variable" concept in Ruby. In any case, the OP's problem seemed puzzling, and wasn't really addressed in any of the answers heretofore, except for a hint in kch's answer: it's a problem of scope. (Added on edit: Actually, sris's answer does address this point at the end, but I'll let this answer stand anyway, as I think the example code might be useful for understanding the problem.)

    In a Ruby class, a variable name starting with @ can refer to one of two variables: either to an instance variable or to a class instance variable, depending on where in the class it's referred to. This is a fairly subtle gotcha.

    An example will clarify the point. Here's a little Ruby test class (all code tested 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
    

    I named the variables according to what I thought they were, though that turns out to not to always be the case:

    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
    => #
    
    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
    

    The @@class_variable and @instance_variable always behave as you'd expect: the former is defined on the class level, and whether referred to in a class method or in an instance method, it holds value assigned to it at the top. The latter only gets a value in an object of class T, so in a class method, it refers to an unknown variable whose value is nil.

    The class method imaginatively named class_method outputs the values of @@class_variable and the two @class_instance_variables as expected, that is, as initialized at the top of the class. However, in the instance methods initialize and instance_method, different variables of the same name are accessed, that is, instance variables, not class instance variables.

    You can see that the assignment in the initialize method did not affect the class instance variable @class_instance_variable_1, because the later call of class_method outputs its old value, "WTF". Instead, method initialize declared a new instance variable, one which is also named (misleadingly) @class_instance_variable_1. The value assigned to it, "wtf", is output by methods initialize and instance_method.

    The variable @class_instance_variable_2 in the example code is equivalent to variable @hello in the original problem: it's declared and initialized as a class instance variable, but when an instance method refers to a variable of that name, it actually sees an instance variable with the same name -- one which was never declared, so its value is nil.

提交回复
热议问题