如何将一个实例变量添加到类中以支持添加的方法?

时间:2012-07-28 22:53:32

标签: ruby metaprogramming

我是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

第一个放置印刷品'1000'

第二个会打印'nil',因此instance_variable_set中的super_accessor_wow来电似乎无效。

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

2 个答案:

答案 0 :(得分:1)

在课程定义期间致电instance_variable_set时,系统会调用super_accessor_wow。目前还没有该类的实例。您在调用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"]

答案 1 :(得分:0)

如评论中所述,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"]