by agate - Published: 2008-07-12 [10:36 下午] - Category: 程序编码

从实用角度看,在一些场景下Symbol好过String只有两点:节省内存,表达简洁。
而把两者强制拆开,既有额外好处也带来不便。
好处是充分利用两者的不同进行差异处理,如上面举的异常处理/when的用法。
但不便之处也不少,因为很多情况下需要无区别化对待两者,rails中就大量采用了这种风格,使用者无须担心
attr_accessor :abc 和 attr_accessor 'abc'有什么区别,layout 'main'和layout :main有什么区别(当然,跟layout nil和layout false还是有区别的,一点机智和诡异)。

当用户传过的东东作为Hash的key时,两者的不同让人烦死。
比如,我在每个model里声明一些字段的默认值:
class Person < ActiveRecord::Base
  DEFAULTS = {
    :age => 30
  }
end

在controller里
class PersonController < ActiveController::Base
  def create
    person = Person.new(Person::DEFAULTS.merge(params[:person]))
    person.save!
  end
end

请注意controller里的merge,假定页面没有传入params[:person][:age],则新建的person的age取默认值30.
那么,当页面传了age参数为40呢?按理merge后age应该为40,事实却不总正确。因为params里的key都是以String存储的,而我的DEFAULTS的key是Symbol,则merge后的结果是
{
  :age => 30,
  "age" => 40
}

最终save!以哪个为准,天知道~

rails实现了名为HashWithIndifferentAccess的类(继承自Hash),就是为了解决这一问题。无论开发者传过来的是 String还是Symbol(作为key),它都会把预先统一转化成String进行存取,以实现无差别化对待。细心的人可能已察觉,既然params 里的内容都是以String作为Key,为啥还可以params[:person]这样访问,奥秘就是用了这个类。它的源码在 gems/activesupport-x.x.x/lib/active_support/core_ext/hash/indifferent_access.rb
所以,我上面的model要改成:
class Person < ActiveRecord::Base
  DEFAULTS = HashWithIndifferentAccess.new({
    :age => 30
  })
end

即使有这个类,也让人呕吐不止。曾想对它简单封装,提供Hash#to_indiff,仍不满意,不过,注意这个差别并有意避免仍是当前能做到的。

ruby1.9已经把Symbol作为String的子类实现了,并去除了Symbol#to_int,虽然没有百分之百确定出现在 ruby2.0里,还好离解放不远,这样从语言角度两者的边界就更清晰了,"Goodbye, HashWithIndifferentAccess."
:symbol.is_a? String
http://redhanded.hobix.com/inspect/SymbolIs_aString.html

让人绝倒的理由之一:

Smalltalk’s symbols are a subclass of string. (He adds that this is his most motivating reason to do it.)


said by liusong1111 from javaeye!

Tags: [ , ] - Comments: View Comments
blog comments powered by Disqus