1

私は誰かのコードを調べていますが、彼がこのようなものでクラス評価を行っていることがわかりました

self.class_eval("@default_robot_engine = RobotEngine.new(some_block)")

その後、このようにアクセスされます

self.class_eval("@default_robot_engine")

このコードを理解するには助けが必要です。class_eval を実行するのではなく、@default_robot_engine にアクセスする他の方法はありますか?

Class.instance_variable_names を実行すると、

["@attribute_methods_mutex"、"@generated_attribute_methods"、"@generated_feature_methods"、"@observer_instances"、"@per_page"、"@parent_name"、"@registered_robot_engines"、"@default_robot_engine"、"@primary_key"、"@quoted_primary_key" 、「@locking_column」、「@attribute_methods_generated」、「@table_name」、「@quoted_table_name」、「@arel_table」、「@arel_engine」、「@relation」、「@columns」、「@column_names」、「@columns_hash」 、「@cached_attributes」、「@attribute_method_matchers_cache」、「@ generated_external_attribute_methods」]

ClassName.registered_robot_engineを除いて、このようにすべてのインスタンス変数にアクセスできますdefault_robot_engine。なぜ?

このインスタンス変数は動的なものであり、attr_readerが設定されていないため、答えがわかりました。アクセスする唯一の方法は、class_evalを介することだと思います

4

4 に答える 4

2

これは特に奇妙なコードです。まず、self.class_evalまったく必要ありません。プレーンclass_evalで十分です。そのプログラマーは Ruby 以外の言語に慣れていたのでしょう。Ruby では、符号で終わるメソッドを呼び出す場合や、呼び出されたメソッドがパブリック メソッドであることを確認するself場合など、まれな場合にのみ明示的なレシーバーを使用します (明示的なレシーバーで呼び出された場合、プライベート メソッドは失敗します)。=

次に、次のように、プログラマーが標準の getter と setter を使用しなかった理由を想像するのは困難です。

class << self
  attr_accessor :default_robot_engine
end

# Here is the case when its legal to use explicit self receiver:
self.default_robot_engine = RobotEngine.new( some_block )

後で簡単にアクセスできます

default_robot_engine

Ruby の基本を知らなかった元のプログラマーを強く疑っています。アクセサーを定義せずにインスタンス変数を改ざんする理由がある場合でも、メソッドclass_evalを使用して , buyt経由ではなく、次のように変更することをお勧めし#instance_variable_get/setます。

instance_variable_set :@default_robot_engine, RobotEngine.new( some_block )
instance_variable_get :@default_robot_engine

クラス評価は、この場合には大きすぎるハンマーのように思えます。

于 2013-08-08T07:59:50.510 に答える
1

うわー、これは楽しいものです。

1.9.3-p429 :094 > class C; self.class_eval "a=3;@b=4;@@c=5"; end
 => 5 
1.9.3-p429 :095 > C.class_variables
 => [:@@c] 
1.9.3-p429 :096 > class C; puts self.class_eval "a+@b+@@c"; end
NameError: undefined local variable or method `a' for C:Class
from (irb):96:in `class_eval'
from (irb):96:in `class_eval'
from (irb):96:in `<class:C>'
from (irb):96
from /Users/cphoenix/.rvm/rubies/ruby-1.9.3-p429/bin/irb:16:in `<main>'
1.9.3-p429 :097 > class C; puts self.class_eval "@b+@@c"; end
9
 => nil 
1.9.3-p429 :098 > 
1.9.3-p429 :098 > C.object_id
 => 2151815060 
1.9.3-p429 :099 > C.class_eval "puts self.object_id"
2151815060
 => nil 
1.9.3-p429 :100 > 

これが起こっているようです。C.class_eval を実行すると、クラスのコンテキストでコードが実行されます。自分自身がクラスです。

C.class_variables と言うと、クラス変数のように見えるものを出力します。これは、094 行で定義した 3 つの変数のうち、@@c だけです。

したがって、この self.class_eval は、@ を 2 つではなく 1 つだけ使用してクラス変数を定義する方法であると推測しています。

a+@b+@@c が a を見つけられない理由はわかりませんが、@b+@@c は両方の変数を見つけます。したがって、これは部分的な答えにすぎないと思います... @b が @@c とは別の場所に保存されているかどうかはわかりません。また、a がどうなるかわかりません。

これは Ruby の奇妙さかもしれません。

于 2013-08-08T07:47:21.533 に答える
0

これを読んで理解してみてください:

http://www.jimmycuadra.com/posts/metaprogramming-ruby-class-eval-and-instance-eval

とても便利です。

于 2013-08-08T07:18:31.837 に答える
0

default_robot_engine を除く、この ClassName.registered_robot_engine のようなすべてのインスタンス変数にアクセスできます。なぜ?

class Dog
  class<< self
    attr_accessor :registered_robot_engine

    def set_stuff
      @registered_robot_engine = 'hello'
      @default_robot_engine = 20
    end
  end
end

Dog.set_stuff
puts Dog.registered_robot_engine
puts Dog.default_robot_engine

--output:--
hello
1.rb:16:in `<main>': undefined method `default_robot_engine' for Dog:Class (NoMethodError)

ruby の基本的なルールは、すべてのインスタンス変数がデフォルトでプライベートであるため、インスタンス変数にアクセサ メソッドを提供しない限りアクセスできないことです。上記の例では、@default_robot_engine に対して定義されたアクセサ メソッドがないため、アクセスできませんが、他のインスタンス変数にはアクセサ メソッドが定義されているため、アクセス可能です。

class_eval() と instance_eval() の両方で、カプセル化に違反し、プライベート インスタンス変数を読み書きできます。

class Dog
  class <<self
    def set_stuff
      @registered_robot_engine = 'hello'
      @default_robot_engine = 20
    end

    def set_more_stuff
      class_eval do 
        @default_robot_engine = 100 
      end
    end
  end
end

Dog.set_stuff
Dog.set_more_stuff

puts Dog.class_eval{ @default_robot_engine }

--output:--
100

instance_variable_set() と instance_variable_get() を使用すると、同じことができます。

class Dog
  def initialize
    @name = "Rover"
  end
end

d = Dog.new
d.instance_variable_set(:@name, "John")
puts d.instance_variable_get(:@name)

--output:--
John

次に、次のように、プログラマーが標準の getter と setter を使用しなかった理由を想像するのは困難です。

class << self
  attr_accessor :default_robot_engine
end

プログラマーが他の誰かのモジュールを使用していて、プログラマーがカプセル化に違反することを決定したと推測しますが、これは ruby​​ で許可されています。一部の言語では、カプセル化は適切ですが、厳密に強制するべきではないと考えています。何らかの理由でプログラマーがカプセル化に違反したい場合は、そうする自由が必要です。

于 2013-08-08T08:39:37.270 に答える