使用类常量作为 Ruby 哈希键

4

我知道可以将类作为哈希键,但这是一个好的实践吗?在性能或测试方面有什么缺点吗?

{
  SomeClassA: 'hash value',
  AnotherClass: 'hash value'
}

只是好奇,你的使用场景是什么?为什么有这样的要求? - vee
4个回答

9
{
  SomeClassA: 'hash value',
  AnotherClass: 'hash value'
}

实际上等同于:
{
  :SomeClassA => 'hash value',
  :AnotherClass => 'hash value'
}

键是符号。在“新”的字面哈希语法中,键只被视为文字,它们会被转换为符号(前提是它们是有效的语法)。

如果要使用常量、范围或任何其他类型的对象作为键,则需要使用hashrockets:

{
  SomeClassA => 'hash value',
  AnotherClass => 'hash value'
}

这是一种好的做法吗?

这是一种在少数情况下可以使用的技术,例如用于替换一系列if语句。

def foo(bar)
  if bar.is_a? SomeClassA
    'hash value'
  end
  if bar.is_a? AnotherClass
    'another value'
  end
end

def foo(bar)
  {
    SomeClassA => 'hash value',
    AnotherClass => 'another value'
  }[bar]
end

但是,我更愿意在这里使用 case 语句,因为这样更清晰明了,而且更灵活。

从性能或测试角度来看,是否存在任何缺点?

你创建的每个哈希都会有指向内存中完全相同对象的键,就像使用符号一样。

Rails 的一个大陷阱是将 const_missing 修改为自动加载文件 - 当引用一个类名时,Rails 将从文件系统加载文件到内存中。这就是为什么要使用以下方式声明关联:

class Foo < ApplicationRecord
  belongs_to :bar, class_name: "Baz"
end

它使得Rails可以在需要时延迟加载Baz。您可以通过以下方式执行与上面示例相同的操作:
def foo(bar)
  {
    'SomeClassA' => 'hash value',
    'AnotherClass' => 'another value'
  }[bar.name]
end

谢谢,这就是我要找的,但是 bar.name.to_sym 如何解决自动加载问题?你能举个例子吗?如果只传递 foo(SomeClassA),在大多数情况下似乎都没问题? - user1883793
“...强制转换你放入键中的任何内容..”可能有点过于强硬。对于字符串、常量和本地变量和方法的名称(没有前导冒号),是可以的,但据我所知,对于其他任何东西(整数、浮点数、符号、实例、类和全局变量的名称、数组、哈希等等)都不行。您可能希望将其更具体化。除了这个小问题外,回答非常好。 - Cary Swoveland
@CarySwoveland 的观点是正确的。它只是将以任何字母开头的字符串强制转换为符号,因为其他任何内容都会导致语法错误,例如 { 0_foo: 2 } - max
1
那是因为 :0_foo 是无效的。你必须使用 :'0_foo'{ '0_foo': 2 } - Stefan
1
是的,在解析哈希时执行语法检查。但是相同的规则应该适用。(如果我没记错,有一个Ruby版本无法解析带引号的符号键) - Stefan
显示剩余2条评论

4

这个哈希表使用符号作为键而不是类,但你可以通过以下方式使用类(Class)

hash = { SomeClassA => "some value" }

我想不出为什么使用它会比使用其他对象更糟糕,因为

Ruby中的类是一级对象——每个类都是Class类的实例。

所以

{ SomeClassA => "some value" }

是功能上等同于

{ "Some String" => "some value" }

2
你手头的不是类键,而是符号。 尝试:
class A
end

class B
end

hash = {A: "Some", B: "Code"}
hash.keys[0].class
=> Symbol

但是仍然 A.class => Class

关于符号性能,这篇文章非常棒

此外,您可以查看有关将用户定义的类用作哈希键的 Ruby 文档。


+1 但是你回答中声明的A类和B类并不相关,有点令人困惑,你觉得呢? - vee

0

虽然你可以这样做,但我想不出使用它的理由。使用符号或字符串作为键更加高效,因为程序不必加载整个类。(顺便提一下,正如其他人指出的那样,你的示例实际上正在使用符号键,如果要使用类名作为键,则需要使用哈希箭头)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接