【问题标题】:How to add an instance variable to a class to back an added method?如何将实例变量添加到类以支持添加的方法?
【发布时间】:2012-07-28 22:53:31
【问题描述】:

我是 Ruby 新手,对如何在 Class 中创建类似于 :attr_accessor 的方法感到困惑,因为它将方法添加到用户类,但是这些添加的方法可以访问预初始化的实例变量。我很难解释,所以这里是我努力的一个大大简化的示例:

class Class
    def super_accessor_wow(attr_name)
        attr_name = attr_name.to_s 
        new_var_name = "@crazy_var_name"

        instance_variable_set(new_var_name, ["hi", "everyone"])

        module_eval(%Q/
            def super_#{attr_name}()
                return @#{attr_name}
            end

            def super_#{attr_name}=(value)
                @#{attr_name} = value
            end

            def greetings
                return #{new_var_name}
            end
        /)
    end
end

这就是我尝试使用Class 上的新方法来修改我自己的类的方式:

class Foo
    super_accessor_wow(:bar)
end

foo1 = Foo.new()
foo1.super_bar = 1000

puts foo1.super_bar
puts foo1.greetings.inspect

第一个 puts 打印 '1000'

第二个puts打印'nil',所以我的instance_variable_set调用super_accessor_wow似乎没有效果。

顺便说一下,我预计第二个 puts 会打印 '['hi', 'everyone']'。所有这些代码都包含在一个 Ruby 文件中。

【问题讨论】:

  • 我认为您正在寻找 instance_variable_set(new_var_name.to_sym, ["hi, "everyone"]) 因为 instance_variable_set 需要一个符号,而不是字符串。不幸的是,这并不能解决问题。

标签: ruby metaprogramming


【解决方案1】:

在类定义期间调用super_accessor_wow 时会调用您的instance_variable_set。该类的实例尚不存在。当您调用new 时,您将创建该类的一个实例。您可以将 @crazy_var_name 初始化添加到构造函数中,或者您可以在 greetings 方法中定义它:

将默认值放在类变量中,并在构造函数中初始化实例变量(请注意,这会为您的类创建一个构造函数,如果您随后创建自己的构造函数,它将覆盖这个):

class Class
    def super_accessor_wow(attr_name)
        attr_name = attr_name.to_s
        new_var_name = "@crazy_var_name"
        new_var_name_default = "@#{new_var_name}"

        module_eval(%Q/
            #{new_var_name_default} = ["hi", "everyone"]

            def initialize()
              #{new_var_name} = #{new_var_name_default}
            end

            def super_#{attr_name}()
                return @#{attr_name}
            end

            def super_#{attr_name}=(value)
                @#{attr_name} = value
            end

            def greetings
                return #{new_var_name}
            end
        /)
    end
end

class Foo
    super_accessor_wow(:bar)
end

foo1 = Foo.new()
foo1.super_bar = 1000

puts foo1.super_bar
puts foo1.greetings.inspect
puts Foo.class_variable_get('@@crazy_var_name').inspect
puts foo1.instance_variable_get('@crazy_var_name').inspect

输出:

1000
["hi", "everyone"]
["hi", "everyone"]
["hi", "everyone"]

greetings方法中定义:

class Class
    def super_accessor_wow(attr_name)
        attr_name = attr_name.to_s
        new_var_name = "@crazy_var_name"

        module_eval(%Q/
            def super_#{attr_name}()
                return @#{attr_name}
            end

            def super_#{attr_name}=(value)
                @#{attr_name} = value
            end

            def greetings
                #{new_var_name} = ["hi", "everyone"] unless #{new_var_name}
                return #{new_var_name}
            end
        /)
    end
end

class Foo
    super_accessor_wow(:bar)
end

foo1 = Foo.new()
foo1.super_bar = 1000

puts foo1.super_bar
puts foo1.greetings.inspect

输出

1000
["hi", "everyone"]

【讨论】:

  • 在问候语中创建实例变量是我最终采用的方法,谢谢。你能演示如何将初始化添加到构造函数中吗?我不明白该怎么做。
  • 编辑了我的答案以在构造函数中包含初始化。如果您在类中创建自己的构造函数,它们将覆盖此构造函数。然后,您应该将 @crazy_var_name = @@crazy_var_name 放入您自己的构造函数中。或者,如果您的实例从不修改此值而只读取它,那么您甚至不需要实例变量或构造函数,只需在需要读取时使用类变量即可。
  • 我明白了,谢谢加里。看起来这种整体方法非常脆弱,并且不是很开放,很高兴知道。
  • 也许继承或混合更适合您正在寻找的功能。
【解决方案2】:

如评论中所述,instance_variable_set 采用符号,而不是字符串,因此我们将首先修复它。

instance_variable_set(new_var_name.to_sym, ["hi", "everyone"])

但最大的问题是 instance_variable_set 不是由 Foo 的实例调用的,而是由 Foo 类本身调用的。因此,正在设置一个实例变量,但不是您所期望的。

Foo.instance_variable_get(:@crazy_var_name).inspect
# ["hi", "everyone"]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-06-27
    • 2021-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多