我想用一个用户指定的块替换对象的方法的实现.在
JavaScript中,这很容易实现:
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,但我真的不喜欢开销和语法
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方法,但我不喜欢这样:).
class Foo def bar x if (@bar.nil?) puts x else @bar.call x end end def set_bar &blk @bar = blk end end
所以问题是:有没有更好的方法来做到这一点,如果没有,你更喜欢什么方式
编辑:
@ welldan97的方法有效,但我松散了局部变量范围,即
prefix = "*" def foo.bar x puts prefix + x.to_s end
不起作用.我想我必须坚持使用lambda才能工作?
解决方法
使用def:
foo = Foo.new foo.bar "baz" def foo.bar x puts "*" + x.to_s end foo.bar "baz"
是的,那么简单
编辑:为了不松开范围,您可以使用define_singleton_method(如@freemanoid答案):
prefix = "*" foo.define_singleton_method(:bar) do |x| puts prefix + x.to_s end foo.bar 'baz'