Ruby переписывание << метод - PullRequest
       3

Ruby переписывание << метод

0 голосов
/ 12 октября 2010

У меня есть 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

Ответы [ 3 ]

5 голосов
/ 12 октября 2010

Вот сложная часть, которую вам не хватает: переменная @b не меняет в вашем примере.Он по-прежнему содержит тот же строковый объект, который вы изначально установили.Это сама строка , которая меняется.Это различие чрезвычайно важно, и если вы его не поймете, вы обнаружите, что ваша программа страдает от тысячи тонких ошибок.Вот оно:

Объекты и переменные - это две независимые вещи.Переменные похожи на слоты, а объекты - это то, что вы в них помещаете.Только оператор = помещает новый объект в слот *;все остальное отправляет сообщения объекту в слоте.Когда вы пишете @thing = "hello", это помещает объект String "hello" в слот @thing.Когда вы пишете @thing << " world", вы не устанавливаете @thing для содержания нового объекта;вы оставляете там тот же объект, но добавляете " world" в конец строки, которую представляет объект.Это также означает, что любые другие слоты, содержащие тот же объект, также обнаружат, что их строки изменились!

Если вы хотите обойти это, вам придется использовать прокси-объект (чтобы получить сообщение <<)как ormuriauga, описанный вместо непосредственного сохранения строки.Ruby's Delegator может быть полезен для этого.Хотя я бы посоветовал подумать, действительно ли вам это нужно, потому что это усложняет ваш дизайн, и часто есть лучший способ сделать это.

* ОК, это немного волнисто.Также есть специальный метод instance_variable_set, который может устанавливать переменные экземпляра.Но не было бы никакого способа написать этот метод самостоятельно без использования операторов = и eval().

1 голос
/ 12 октября 2010

Вам нужно будет сделать 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, а затем вызываете << для этого объекта и, таким образом, только когда-либо взаимодействует с вашим классом A при доступе к b. Я переопределил этот метод << для b.

0 голосов
/ 12 октября 2010

Ваш метод b не был вызван на линии прощания.

Вызван метод доступа с нулевым параметром, который вы определили, и возвращенная строка была передана методу << класса String.

Вы определили два метода с похожими именами: один b, а другой b=.

В первом вызове метода вы действительно вызвали метод b=, а затем во втором вы вызвали обычный b. Попробуйте это:

a.b = "hello world!"
success=> "hello world!"
a.b = '' << " and goodbye!"
success=> " and goodbye!"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...