【问题标题】:Where did this value variable come from?这个值变量是从哪里来的?
【发布时间】:2019-12-05 02:57:39
【问题描述】:

我正在阅读如何使用元编程来制作属性访问器的示例,但我对这个值变量的来源有点困惑:

class AttrAccessorObject
  def self.my_attr_accessor(*names)
    names.each do | name |
      define_method(name) {
        self.instance_variable_get("@#{name}".to_sym)
      }
      define_method("#{name}=") do | value |
        self.instance_variable_set("@#{name}".to_sym, value)
      end
    end
  end
end

我知道instance_variable_set方法需要实例变量和值来设置实例变量的新值,但是代码中的这个值变量是从哪里来的呢?另外,由于它使用“do/end”循环来使用该值,我假设“define_method("#{name}=") 评估为一个值数组,对吗?

【问题讨论】:

  • value 是方法签名的一部分。这意味着定义的方法将接受一个参数。如果您使用两个变量define_method("#{name}_with_two") do | value1, value2 | - 定义的方法将接受两个参数。
  • do ... end 不是循环。它是一个块,在 Ruby 中是一个无处不在的概念。在阅读 Ruby 之前,您必须了解块。例如,here。具体来说,define_method 定义了一个方法,并将块附加为方法的主体。
  • @Fabio 啊哈,我明白了。那么“值”本质上就是将传递给已定义方法的参数?
  • @Karen:正是如此。
  • 太棒了!感谢您对块的解释和澄清!

标签: ruby metaprogramming attr-accessor


【解决方案1】:

如果您想到“正常”方法定义的样子:

def some_name=(value)
  @some_name = value
end

|value| 部分与(value) 相同 - 它声明了您定义的方法所接受的参数。因此,如果我想创建一个需要 3 个参数的方法:

define_method(:some_method) do |arg1, arg2, arg2|
  [arg1, arg2, arg3]
end

另外,由于它使用“do/end”循环来使用该值,我假设“define_method("#{name}=") 计算为一个值数组,对吗?

不,块不仅仅与数组一起使用。将块想象成一个匿名函数,用作另一个函数的参数。您调用的函数(例如each)可能会在内部调用匿名函数 0、1 或任意次数。对于each,它调用了N次(每个数组元素调用一次):

def my_each(array, &blk)
  for elem in array do
    blk.call(elem)
  end
end

但对于tap,它只调用一次:

def my_tap(obj, &blk)
  blk.call(obj)
  obj
end

【讨论】:

    【解决方案2】:

    value 是您定义的方法签名的一部分。

    define_method 可以解释为在代码块中添加“标签”并将其引用保存在类中以供以后使用。

    define_method("print_two_values") do | value1, value2 |
        puts "first: #{value1}, second: #{value2}"
    end
    

    可以改写为

    def print_two_values(value1, value2)
        puts "first: #{value1}, second: #{value2}"
    end
    

    【讨论】:

      【解决方案3】:

      不能不说引用的代码存在一些弱点。

      1. Object#instance_variable_getObject#instance_variable_set 的第一个参数无需转换为符号。

      2. (挑剔)self. 不需要在 instance_variable_getinstance_variable_set 之前,因为在没有显式接收者的情况下调用方法时,self 是隐含的接收者。

      3. (主要)该方法不必要地复杂,因为使用 Ruby 的方法 Module#attr_accessor 更容易,该方法专门用于创建 getter 和 setter 方法:

      class AttrAccessorObject
        def self.my_attr_accessor(*names)
          attr_accessor(*names)
        end
      end
      

      AttrAccessorObject.my_attr_accessor "pig", "owl"
        #=> ["pig", "owl"] 
      AttrAccessorObject.instance_methods(false)
        #=> [:pig, :pig=, :owl, :owl=] 
      inst = AttrAccessorObject.new
        #=> #<AttrAccessorObject:0x00005661152d6ed8> 
      inst.owl = 'hoot'
        #=> "hoot" 
      inst.owl
        #=> "hoot" 
      

      【讨论】:

      • 4.该方法不必要地复杂,因为Module#attr_accessor 使用 splat 参数作为名称:class AttrAccessorObject; def self.my_attr_accessor(*names) attr_accessor(*names) end end :-P
      • 我认为他们练习的目标是重新创建attr_accessor,所以调用内置函数会失去重点
      • @JörgWMittag,好点子。我不知道我是怎么错过的。我编辑了。
      • @maxpleaner,这是可能的,但没有提到这一点,我的第 1 点告诉我,`attr_accessor' 的使用可能被忽略了。
      猜你喜欢
      • 1970-01-01
      • 2018-05-30
      • 1970-01-01
      • 2020-07-31
      • 2019-11-18
      • 2018-09-25
      • 2014-10-23
      相关资源
      最近更新 更多