2010-10-11 115 views
0

我有class A红宝石重写<<方法

class A 
    attr_reader :b 
    def b=param 
    @b = param 
    print "success" 
    end 
end 

>> a = A.new 
>> a.b = "hello world!" 
#> "success" 
#> "hello world!" 
>> a.b << " and goodbye!" 
#> "helo world! and goodbye!" 

哪里是我的 “成功”? :)

我想打印“成功”每当我的变量发生变化。

我不能只是写

def b<<param 
    @b << param 
    print "success" 
end 

回答

5

这里是你错过了棘手的部分:变量@b不会在你的例子改变。它仍然包含您最初设置为的相同字符串对象。这是字符串本身正在改变。这种区别是非常重要的,如果你不掌握它,你会发现你的程序受到千变万化的困扰。这里是:

对象和变量是两个独立的东西。变量就像插槽一样,对象是你放入它们的东西。只有=操作员将新对象放入插槽*;其他一切都会将消息发送给插槽中的对象。当您编写@thing = "hello"时,会将字符串对象"hello"放入插槽@thing中。当你写@thing << " world"时,你并没有设置@thing来包含一个新的对象;您将同一对象留在那里,但将" world"添加到该对象所表示的字符串的末尾。这也意味着任何其他插槽持有相同的对象也会发现他们的字符串改变!

如果你想解决这个问题,你必须使用代理对象(接收<<消息),如ormuriauga described,而不是直接存储字符串。 Ruby的Delegator可能对此有用。虽然我会建议考虑你是否真的需要这个,因为它确实使你的设计复杂化,并且通常有更好的方法来做到这一点。

*好的,这有点手摇。还有可以设置实例变量的特殊instance_variable_set方法。但是,如果不使用=运营商eval(),则无法自行编写该方法。

+0

thanx,这是非常有趣的信息 – fl00r 2010-10-11 21:57:50

+0

除了'instance_variable_set',还有'instance_eval'。但这是你不需要使用的黑魔法。 – 2010-10-14 22:36:44

1

你将不得不作出b是一个类你自己的,你定义所有你想要做你想要的方法。

虽然可以,其实,做这样的事情:

class A 
    attr_reader :b 
    def initialize 
     @b = "" 
     augment_b! 
    end 

    def augment_b! 
     class << @b 
     alias_method :ltlt, :<< 
     def << args 
      self.ltlt args 
      print "success" 
     end 
     end 
    end 
    def b=param 
    @b = param.to_s 
    augment_b! 
    print "success" 
    end 
end 

我不推荐,虽然它。

让我们来看看我能否向您解释这一点。

当你分配给b属性b=方法被调用。但是当您拨打a.b <<时,您正在访问b,然后在该对象上调用<<,因此只有在访问b时才与您的A类接口。我所做的是在b上重新定义了<<方法。

+0

a.b << 'd' =>'SystemStackError:stack level too deep' – fl00r 2010-10-11 21:41:15

+0

是的,对不起。现在修复它。没有测试过 – einarmagnus 2010-10-11 21:43:30

+0

看起来太hacky :)是不是在Ruby中更简单的方式来检查实例变量的访问和更改? – fl00r 2010-10-11 21:50:41

0

你的方法b没有叫上再见线。

你定义的零参数的访问方法被调用,并返回的字符串传递给String类的<<方法。

您已经定义了两种方法具有类似名称:一种是b,另一个是b=

在第一个方法调用中,您确实调用了b=方法,然后在第二个方法中调用了纯文本b。试试这个:

a.b = "hello world!" 
success=> "hello world!" 
a.b = '' << " and goodbye!" 
success=> " and goodbye!" 
+0

但它以某种方式与'b'对齐。调用什么方法进行分配? setter我想 - 所以在我的二传手我打电话'print' – fl00r 2010-10-11 21:38:29

+0

好吧,我已经添加了一个更新。有趣的是,注意到具有嵌入空格的方法名称的创造性解析。 – DigitalRoss 2010-10-11 21:47:33

+0

正如我前面提到的 - 如果我无法控制我的实例变量状态并通过难以从'=' – fl00r 2010-10-11 21:52:35