在Ruby中动态替换对象上的方法实现 [英] Dynamically replace method implementation on an object in Ruby

查看:74
本文介绍了在Ruby中动态替换对象上的方法实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用用户指定的块替换对象的方法实现.在JavaScript中,这很容易实现:

I'd like to replace the implementation of a method for an object with a block that the user specifies. In JavaScript, this is easily accomplished:

function Foo() {
    this.bar = function(x) { console.log(x) }
}
foo = new Foo()
foo.bar("baz")
foo.bar = function(x) { console.error(x) }
foo.bar("baz")

在C#中也很容易

class Foo
{
    public Action<string> Bar { get; set; }
    public Foo()
    {
        Bar = x => Console.WriteLine(x);
    }
}

var foo = Foo.new();
foo.Bar("baz");
foo.Bar = x => Console.Error.WriteLine(x);
foo.Bar("baz");

但是我该如何在Ruby中做同样的事情?我有一个解决方案,将lambda存储在实例变量中,该方法调用lambda,但我真的不喜欢开销和语法

But how can I do the same in Ruby? I have a solution that stores a lambda in an instance variable and the method calls the lambda, but I don't really like the overhead and syntax

class Foo
  def initialize
    @bar = lambda {|x| puts x}
  end

  def bar x
    @bar.call x
  end

  def bar= blk
    @bar = blk
  end
end

foo = Foo.new
foo.bar "baz"
foo.bar= lambda {|x| puts "*" + x.to_s}
foo.bar "baz"

我想要这样的语法:

foo.bar do |x|
    puts "*" + x.to_s
end
foo.bar "baz"

我想出了以下代码

class Foo
  def bar x = nil, &blk
    if (block_given?)
      @bar = blk
    elsif (@bar.nil?)
      puts x
    else
      @bar.call x
    end
  end
end

但是,对于多个参数来说,这有点丑陋,但仍然感觉不到正确".我也可以定义set_bar方法,但我也不喜欢:).

But this is kinda ugly for more than one parameter and still doesn't feel 'right'. I could also define a set_bar method, but i don't like that either :).

class Foo
  def bar x
    if (@bar.nil?)
      puts x
    else
      @bar.call x
    end
  end

  def set_bar &blk
    @bar = blk
  end
end

问题是:有没有更好的方法可以做到这一点?如果没有,您会选择哪种方式

So question is: Is there a better way do to do this and if not, what way would you prefer

@ welldan97的方法有效,但是我松开了局部变量范围,即

@welldan97's approach works, but i loose the local variable scope, i.e.

prefix = "*"
def foo.bar x
    puts prefix + x.to_s
end

不起作用.我想我必须坚持使用lambda才能起作用?

doesn't work. I suppose I have to stick with lambda for that to work?

推荐答案

使用def:

foo = Foo.new
foo.bar "baz"

def foo.bar x
  puts "*" + x.to_s
end

foo.bar "baz"

是的,很简单

要不松开范围,可以使用define_singleton_method(如@freemanoid答案中所示):

To not loose the scope you can use define_singleton_method(as in @freemanoid answer):

 prefix = "*"

 foo.define_singleton_method(:bar) do |x|
   puts prefix + x.to_s
 end

 foo.bar 'baz'

这篇关于在Ruby中动态替换对象上的方法实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆