3

このコードの実行:

module A
  def self.included(klass)
    klass.send(:cattr_accessor, :my_name)
  end

  def set_my_name_var
    @@my_name = 'A' # does NOT work as expected
  end

  def set_my_name_attr
    self.class.my_name = 'A' # works as expected
  end
end

class B
  include A

  cattr_accessor :my_other_name

  def set_my_other_name_var
    @@my_other_name = 'B' # works
  end

  def set_my_other_name_attr
    self.class.my_other_name = 'B' # works
  end
end

b = B.new

b.set_my_other_name_var
puts "My other name is " + B.my_other_name
b.set_my_name_var
puts "My name is " + B.my_name

b.set_my_other_name_attr
puts "My other name is " + B.my_other_name
b.set_my_name_attr
puts "My name is " + B.my_name

このように壊れます:

My other name is B
TypeError: (eval):34:in `+': can't convert nil into String

コードの最後の 2 つのブロックを交換すると ( のb.set_my_name_attr前に呼び出されるようにb.set_my_name_var)、すべて正常に動作します。

classではなく@@my_name、 module のクラス変数として扱うようです(私が期待するように)。混乱しませんか?モジュール クラス変数の詳細はどこで確認できますか?AB

4

1 に答える 1

3

set_my_name_varモジュールにメソッドがある場合、これはモジュール変数を に設定していAます。インクルード クラスを介してメソッドが呼び出された場合、この動作は変わりません。これはまた、時々人々を捕まえる別の事実につながります -複数のクラスに含める場合、含まれるクラスごとに 1 つのインスタンスではなく、 のインスタンスが 1 つだけ存在します。次の例は、これを示しています。@@my_name = 'A'AA@@my_name

module Example
  def name=(name)
    @@name = name
  end

  def name
    @@name
  end
end

class First
  include Example
end

class Second
  include Example
end

irb(main):066:0> f = First.new
=> #<First:0x2d4b80c>
irb(main):067:0> s = Second.new
=> #<Second:0x2d491d8>
irb(main):068:0> f.name = 'Set via f'
=> "Set via f"
irb(main):069:0> s.name
=> "Set via f"

アップデート

期待どおりに機能しないように見える理由を説明するために何が起こっているのかを理解したと思います。cattr_reader(および拡張cattr_accessor) には以下が含まれます。

class_eval(<<-EOS, __FILE__, __LINE__)
  unless defined? @@#{sym}  # unless defined? @@hair_colors
    @@#{sym} = nil          #   @@hair_colors = nil
  end

  # code to define reader method follows...

次のシーケンスが行われます。

  • B定義されています
  • モジュールAが含まれています
  • includedコールバックが行いますklass.send(:cattr_accessor, :my_name)
  • に設定されている@@my_nameクラスにanが作成されます。Bnil

cattr_accessorthen after 呼び出しがなければ、その中でset_my_name_var言うと、モジュールの変数を参照します。しかし、in place を使用すると、同じ名前の変数がクラスに存在するため、withinと言うと、 の変数よりも の変数の値が優先されます。これが私がマスキングした意味です。(の変数が の表示を妨げています)@@my_nameBcattr_accessor@@my_nameBBABA

たぶん、以下が説明します。想像してみてくださいb = B.new。次のことを行います。

>> A.class_variables
=> [] # No methods called on A yet so no module variables initialised
>> B.class_variables
=> ["@@my_other_name", "@@my_name"] # these exist and both set to nil by cattr_accessor
>> B.send(:class_variable_get, '@@my_name')
=> nil # B's @@my_name is set to nil
>> b.set_my_name_var # we call set_my_name_var as you did in the question
=> "A"
>> A.send(:class_variable_get, '@@my_name')
=> "A" # the variable in the module is to to 'A' as you expect
>> B.send(:class_variable_get, '@@my_name')
=> nil # but the variable in the class is set to nil
>> B.my_name
=> nil # B.my_name accessor has returned the variable from the class i.e. nil

セッターの前にゲッターを使用しようとすると、エラーcattr_readerを回避するためにこれを行うと思います。uninitialized class variable(クラス変数はnil、インスタンス変数と同じようにデフォルトにはなりません。)

于 2010-07-16T11:30:38.620 に答える