質問

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

h = Hello.new
h.display

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

役に立ちましたか?

解決

Rubyのインスタンス変数は、特にJavaのような別のOO言語に慣れている場合、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メソッドですべてのインスタンス変数を初期化します。どのインスタンス変数を公開する必要があるかを明確に文書化する別の方法は、モジュールメソッド 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の説明のようなもう1つの重要な問題は、クラスを宣言するときにアクティブなさまざまなコンテキストに注意する必要があるということです。クラスを宣言するとき、最も外側のスコープのデフォルトレシーバー(自己)は、クラス自体を表すオブジェクトになります。したがって、コードは、クラスレベルで @hello に割り当てるときに、クラスインスタンス変数を最初に作成します。

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

他のヒント

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

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

h = Hello.new
h.display

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

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

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

The Pragmatic Programmersによる各5ドルのスクリーンキャストのコレクション

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

本には「ルビープログラミング言語」という明確な説明がありますので、読んでください。ここに貼り付けます(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として参照できます。

「クラスインスタンス変数」があることを忘れていました。 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_variable および @instance_variable は常に期待どおりに動作します。前者はクラスレベルで定義され、クラスメソッドで参照されるか、インスタンスメソッドでは、上部に割り当てられた値を保持します。後者は、クラス T のオブジェクトでのみ値を取得するため、クラスメソッドでは、値が nil である不明な変数を参照します。

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

initialize メソッドでの割り当ては、クラスインスタンス変数 @ class_instance_variable_1 に影響しなかったことがわかります。これは、後で class_method を呼び出したためです。古い値&quot; WTF&quot; を出力します。代わりに、メソッド initialize 新しいインスタンス変数を宣言しました。 (誤って) @ class_instance_variable_1 という名前が付けられています。割り当てられた値&quot; wtf&quot; は、メソッド initialize および instance_method によって出力されます。

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

&quot; @@&quot;という接頭辞が付いたクラス変数を確認することもお勧めします。 -クラス変数とインスタンス変数の違いを示すサンプルコードを次に示します。

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
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top