don*_*ndo 5 ruby ruby-on-rails
我偶尔会遇到形式为Array(value),String(value)和Integer(value)的转换.在我看来,这些只是调用相应的value.to_a,value.to_s或value.to_i方法的语法糖.
所以我想知道:
这些可以用于类型通用强制吗?也就是说,我可以做一些事情
[Integer, String, Array].each {|klass| klass.do_generic_coercion(foo) }
Run Code Online (Sandbox Code Playgroud)?(...和不,我真的不想这样做;我知道我想要的类型,但我希望避免案例陈述.)
Mar*_*une 10
这是一个好而难的问题.让我们回答这三个部分.
第一部分
要找到定义,重要的是要意识到方法的名称是"数组"等,这可能是非常违反直觉的,因为方法通常是小写的...
irb> method(:Array)
=> #<Method: Object(Kernel)#Array>
Run Code Online (Sandbox Code Playgroud)
这告诉您这些是在内核中定义的,因此可以在任何地方使用而无需显式前缀.
第二部分
Array(),, String()...是转换方法.调用obj.to_a将返回一个数组,但会引发一个NoMethodErrorif obj不会respond_to? :to_a.因此,典型的情况下,当你更喜欢使用Array(),String()的,而不是to_a或to_s当你不积极的一个对象响应一个给定的转换方法.
String(obj)nil如果obj没有,将返回respond_to? :to_s.String(obj)还将检查to_s的结果实际上是否为字符串; 它应该是,但也许一个过于有创造力的程序员决定返回别的东西?
大多数其他转换方法的行为方式相同,但Array(obj)不同.[obj]如果obj没有,它将返回respond_to? :to_a.它实际上会调用to_ary(这是隐式转换操作,而to_a显式转换操作).
还有另一种重要的方法来转换1.9(和即将到来的1.8.8)中的对象:Array.try_convert(obj).nil如果obj没有,则返回respond_to? :to_ary.它不会打电话给to_a.虽然它们的输入时间较长,但您可能更喜欢在编写可能接受不同类型对象的非常通用的代码时使用它们,并且希望避免错误地将哈希转换为数组(例如,因为Hash有to_a方法但没有to_ary).当你的方法需要一个类似数组的对象并且你愿意进行显式转换时,那obj.to_a很好.典型的用法Array(obj)是在接受单个obj操作的对象或对象列表的方法中(尽管通常将其写为[*obj]).
最后部分
希望前两部分的答案能为您提供最终答案......
您可以使用:
[Integer, String, Array].each {|klass| klass.try_convert(foo) }
Run Code Online (Sandbox Code Playgroud)
要么
[:Integer, :String, :Array].each{|method| send(method, obj)}
Run Code Online (Sandbox Code Playgroud)
好问题!让我们看看我们是否可以搞清楚.
Ross-Harveys-MacBook-Pro:ruby-1.9.1-p376 ross$ irb
irb(main):001:0> Object.ancestors
=> [Object, Kernel]
irb(main):002:0> Kernel.ancestors
=> [Kernel]
irb(main):003:0> Kernel.class
=> Module
irb(main):004:0> Kernel.public_methods.include? "Array"
=> true
Run Code Online (Sandbox Code Playgroud)
因此,看起来这些是内核模块中混合到Object中的方法,因此可以在不指定接收器的情况下使用它们.我们可能还想在object.c中查看C实现:
VALUE
rb_Array(VALUE val)
{
VALUE tmp = rb_check_array_type(val);
if (NIL_P(tmp)) {
tmp = rb_check_convert_type(val, T_ARRAY, "Array", "to_a");
if (NIL_P(tmp)) {
return rb_ary_new3(1, val);
}
}
return tmp;
}
Run Code Online (Sandbox Code Playgroud)
有一件事似乎很容易得出结论,默认.to_a是不推荐使用的,所以看起来似乎Array(x)是进行转换的规范方法.如果给出一个数组,它显然没有任何作用,.to_a如果存在则调用,如果没有,它只是将其参数包装在一个数组中.
关于是否to_a弃用......好吧,我说"默认":
Ross-Harveys-MacBook-Pro:puppet_sd ross$ irb
irb(main):001:0> class X; X; end.new.to_a
(irb):1: warning: default `to_a' will be obsolete
Run Code Online (Sandbox Code Playgroud)