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

h = Hello.new
h.display

上記のクラスを作成しました。何も印刷されません。クラス宣言時にインスタンス変数@helloが設定されていると思いました。しかし、displayメソッドを呼び出すと、出力は「nil」になります。これを行う正しい方法は何ですか?

4

6 に答える 6

96

特にJavaのような別のオブジェクト指向言語に慣れている場合、Rubyのインスタンス変数は、Rubyを最初に学習するときに少し混乱する可能性があります。

インスタンス変数を単純に宣言することはできません。

@記号の接頭辞が付いた表記とは別に、rubyのインスタンス変数について知っておくべき最も重要なことの1つは、それらが最初に割り当てられたときに起動することです。

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

このメソッドObject#instance_variablesを使用して、オブジェクトのすべてのインスタンス変数を一覧表示できます。

通常、initializeメソッドですべてのインスタンス変数を「宣言」して初期化します。公開する必要のあるインスタンス変数を明確に文書化するもう1つの方法は、Moduleメソッドattr_accessor(読み取り/書き込み)、attr_writer(書き込み)、およびattr_reader(読み取り)を使用することです。これらのメソッドは、リストされたインスタンス変数に対してさまざまなアクセサーメソッドを合成します。

class Hello
  attr_accessor :hello
end

h = Hello.new
p h.instance_variables 

h.hello = "hello"
p h.instance_variables

# Output
[]
["@hello"]

Hello#hello=インスタンス変数は、合成されたメソッドの使用に割り当てられるまで作成されません。

説明されているkchのような別の重要な問題は、クラスを宣言するときにアクティブなさまざまなコンテキストに注意する必要があることです。クラスを宣言する場合、最も外側のスコープのデフォルトのレシーバー(self)は、クラス自体を表すオブジェクトになります。したがって、コードは@hello、クラスレベルで割り当てるときに、最初にクラスインスタンス変数を作成します。

メソッド内では、 selfがメソッドが呼び出されるオブジェクトになるため、オブジェクト内に存在しない名前のインスタンス変数の値を出力しようとして@helloいます(存在しないインスタンスを読み取ることは完全に合法であることに注意してください)。変数)。

于 2009-05-06T08:00:53.020 に答える
45

initializeメソッドを追加する必要があります。

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

h = Hello.new
h.display
于 2009-05-05T20:22:24.587 に答える
24

コードの最初の@hello変数は、クラス インスタンス変数と呼ばれます。

これは、定数がHello指すクラス オブジェクトのインスタンス変数です。(そしてこれはクラスのインスタンスですClass。)

技術的には、classスコープ内にいる場合、 yourselfは現在のクラスのオブジェクトに設定され、 current に@variables関連しますself。私はこれらのことを説明するのが苦手です。

The Pragmatic Programmers の各 5 ドルのスクリーンキャストのこのコレクションを見ると、これらすべてをより明確に理解できます。

(または、ここで説明を求めることができます。更新を試みます。)

于 2009-05-05T20:38:17.340 に答える
10

「Ruby プログラミング言語」という本に明確な説明があります。それを読むと非常に役に立ちます。ここに貼り付けます(7.1.16章から):

クラス定義の内部で使用され、インスタンス メソッド定義の外部で使用されるインスタンス変数は、クラス インスタンス変数です

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

……

クラス インスタンス変数はクラス オブジェクトの単なるインスタンス変数であるため、attr、attr_reader、および attr_accessor を使用してそれらのアクセサ メソッドを作成できます。

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

これらのアクセサを定義すると、生データを Point.n、Point.totalX、および Point.totalY として参照できます。

于 2010-11-11T06:29:11.033 に答える
5

Rubyに「クラスインスタンス変数」という概念があることを忘れていました。いずれにせよ、OPの問題は不可解に見え、kchの回答のヒントを除いて、これまでのどの回答でも実際には対処されていませんでした.それは範囲の問題です. (編集時に追加:実際には、srisの回答最後にこの点に対処していますが、コード例が問題を理解するのに役立つと思うので、とにかくこの回答をそのままにしておきます。)

Ruby クラスでは、 で始まる変数名は、クラス内のどこで参照されているかに応じて、インスタンス変数またはクラス インスタンス変数の2 つの変数のいずれかを@参照できます。これはかなり微妙な落とし穴です。

例でポイントを明確にします。これは小さな Ruby テスト クラスです (すべてのコードは 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

変数には、私が思っていたとおりに名前を付けましたが、常にそうであるとは限りません。

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_variableand@instance_variableは常に期待どおりに動作します。前者はクラス レベルで定義され、クラス メソッドで参照されるかインスタンス メソッドで参照されるかに関係なく、先頭に割り当てられた値を保持します。後者は class のオブジェクトでのみ値を取得するTため、クラス メソッドでは、値が である未知の変数を参照しますnil

想像力に富んだ名前のクラス メソッドは、クラスの先頭で初期化されたとおりに、と の 2 つの のclass_method値を出力します。ただし、インスタンス メソッドおよびでは、同じ名前の異なる変数、つまり、クラス インスタンス変数ではなくインスタンス変数にアクセスします。@@class_variable@class_instance_variableinitializeinstance_method

initializeメソッドでの割り当てがクラス インスタンス変数 に影響を与えていないことがわかります@class_instance_variable_1。これは、後の の呼び出しがclass_methodその古い値 を出力するため"WTF"です。代わりに、 methodinitialize 新しいインスタンス変数を宣言しました。それに割り当てられた値は、メソッド およびによって出力されます。@class_instance_variable_1"wtf"initializeinstance_method

サンプル コードの変数は、元の問題の@class_instance_variable_2変数と同等です@hello。クラス インスタンス変数として宣言および初期化されていますが、インスタンス メソッドがその名前の変数を参照すると、実際には同じ名前のインスタンス変数が表示されます。宣言されていないものなので、その値は nil です。

于 2014-12-25T19:03:43.237 に答える
1

また、「@@」で始まるクラス変数を確認することをお勧めします。クラス変数とインスタンス変数の違いを示すサンプル コードを次に示します。

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
于 2011-02-20T15:56:15.520 に答える