2011-10-28 68 views
3

如何从单例方法访问实例变量?从单例方法访问实例变量

class Test 
    def initialize(a) 
    @a = a 
    end 

    def item 
    item = "hola" 
    def item.singl 
     [self, @a].join(" + ") 
    end 
    item 
    end 
end 

test = Test.new("cao") 
item = test.item 
item.singl 
#=> ... @a is nil 

回答

6

尝试使用define_method。 Def让你进入一个新的范围。

class Test 
    def initialize(a) 
    @a = a 
    end 

    def item 
    item = "hola" 
    item.singleton_class.send(:define_method, :singl) do 
     [self, @a].join(" + ") 
    end 

    item 
    end 
end 

test = Test.new("cao") 
item = test.item 
item.singl #=> "hola + " 

在你的例子中,你仍然有一个问题,在一个字符串@a的单例类中还没有定义。这主要是因为在这种情况下self是字符串实例,而不是存在@a的测试实例。要解决这个问题,你可以将实例变量重新绑定到别的东西上,但这可能不是你想要的行为。你也可以在你的新单例类中设置实例变量。

例如,

重新绑定变量

class Test 
    def initialize(a) 
    @a = a 
    end 

    def item 
    item = "hola" 
    new_self = self 
    item.singleton_class.send(:define_method, :singl) do 
     [self, new_self.instance_variable_get(:@a)].join(" + ") 
    end 

    item 
    end 
end 

test = Test.new("cao") 
item = test.item 
item.singl 

设置一个实例字符串变量

class Test 
    def initialize(a) 
    @a = a 
    end 

    def item 
    item = "hola" 
    item.singleton_class.send(:define_method, :singl) do 
     [self, @a].join(" + ") 
    end 

    item.singleton_class.send(:define_method, :set) do 
     @a = "cao" 
    end 

    item 
    end 
end 

test = Test.new("cao") 
item = test.item 
item.set 
item.singl 

其重要的要注意这两种方法之间的差异。在第一种方法中,我们通过原始对象保留对原始实例变量的引用。在第二种方法中,我们创建一个新的实例变量,绑定在新的单例类下,包含原始测试的副本。

如果您使用的是非本地对象,那么您可能会混合使用这两种方法。编辑:作为Benoit指出,在第二种方法“set”方法应该只是一个attr_accessor。实际上,你可以设置实例变量而不需要定义新的方法。通过item.instance_variable_set(:@, "cao")

+0

我无法理解你的最后一个例子,你正在对项目类中的“cao”字符串进行硬编码,而不是获取传递给Test初始值设定项的内容。你是否想将它定义为属性设置器? –

+0

我很懒。我用这个区别来说明这样一个事实,即用第二种方法,两个@ a不一样。我认为这个方法可以推断出这个方法“应该”是一个属性设置器。我会澄清。谢谢! – diedthreetimes

1

你想设置的Test类的实例变量和字符串实例检索,这些都是不一样的对象和不共享实例变量。您可以执行以下操作以在两个实例之间传递它:

class Test 
    def initialize(a) 
    @a = a 
    end 

    def item 
    item = "hola" 
    item.singleton_class.send :attr_accessor, :a 
    # set the item's @a with the Test instance one 
    item.a = @a 
    def item.singl 
     [self, @a].join(" + ") 
    end 
    item 
    end 
end 

test = Test.new("cao") 
item = test.item 
puts item.singl 
+0

在你的例子中,你有一个小问题。新项目引用@a的副本,而不是@a本身。如果test.a发生变化,这是一个探针。例如,这会导致不正确的结果。 item.singl; test.a =“hi”; item.singl。当然,这真的只适用于字符串... – diedthreetimes

+0

感谢您的警告,但我不会触及我的答案。你的已经比我好多了;-) –

1

您并不试图访问item的实例变量。 itemString对象,而@aTest对象test的实例变量。

两者都是独立的。访问来自item@a的唯一方式是在item例如test(或@a)中提及。

class Test 
    attr_reader :a 
    def initialize(a)  
    @a = a 
    end  

    def item  
    item = "hola" 
    def item.singl  
     [self, @parent.a].join(" + ")  
    end 
    item.instance_variable_set(:@parent, self) 

    item  
    end 
end 

test = Test.new("cao") 
item = test.item 
item.singl