【发布时间】:2012-02-16 02:00:22
【问题描述】:
我正在使用 IronRuby 在我的应用程序中编写一些脚本。
我有很多命令(CLR 对象)需要创建和执行,但大多数命令都需要在它们上设置属性,我希望避免将命令分配给变量,以便我可以设置属性。
有没有办法像 C# 对象 initializer syntax 那样做?
【问题讨论】:
我正在使用 IronRuby 在我的应用程序中编写一些脚本。
我有很多命令(CLR 对象)需要创建和执行,但大多数命令都需要在它们上设置属性,我希望避免将命令分配给变量,以便我可以设置属性。
有没有办法像 C# 对象 initializer syntax 那样做?
【问题讨论】:
在构造 CLR 对象时没有内置的设置属性的方法,因为它不是 Ruby 本身的特性。然而,尽管 Python 也不支持这个,IronPython does support it by allowing named parameters to be supplied to constructors。由于 Ruby 不支持命名参数,我们不想强制使用命名参数模式,无论是作为最后一个参数的 Hash,还是针对构造对象的 instance_eval'd 块,或者其他人来跟上。
话虽如此,实现对象初始化器的两种策略都可以用纯 Ruby 编写,而无需在 IronRuby 中提供任何特殊支持。我将描述上面列出的两者,但如果它们与您正在寻找的语法不完全相同,请随意尝试。
假设以下 CLR 类(用 C# 编写):
namespace TestLib {
public class TestObj1 {
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
}
您可以想象使用传递给构造函数的块来初始化属性:
testobj = TestObj1.new do
self.prop1 = "Prop1 Value"
self.prop2 = "Prop2 Value"
end
您可以通过以下方式覆盖 TestObj1.new 方法来支持它:
class TestObj1
def self.new(*args, &block)
obj = super(*args)
obj.instance_eval &block
obj
end
end
由于这实际上是evals 块针对新创建的对象,因此您可以在块内执行任何 Ruby 代码。在 Ruby 中创建 DSL 和更自然的 API 时,这种模式很流行。
或者,如果您更喜欢在方法参数中使用 Ruby 宽松的 Hash 语法:
testobj = TestObj1.new :prop1 => "Prop1 value", :prop2 => "Prop2 value"
那么这就是您可以覆盖.new 方法的方法:
class TestObj1
def self.new(*args)
last_arg = args[-1]
if last_arg.kind_of?(Hash)
first_args = args[0..-2]
obj = super(*first_args)
last_arg.each do |prop, val|
obj.send("#{prop}=", val)
end
return obj
end
super(*args)
end
end
Hash 选项肯定更复杂一些,但性能更高一些(因为它避免了eval),并且是 Ruby 中公开命名参数的更常见模式。
【讨论】: