我什么时候应该使用Struct vs. OpenStruct?

ehs*_*nul 179 ruby struct

总的来说,与Struct相比,使用OpenStruct有什么优缺点?什么类型的一般用例适合这些?

Pes*_*sto 169

使用OpenStruct,您可以随意创建属性.一Struct,在另一方面,必须在创建它的属性来定义.一个选择另一个应该主要取决于您是否需要以后能够添加属性.

考虑它们的方式是作为一方面的哈希与另一方的类之间的频谱的中间立场.它们意味着数据之间的关系比a更具体Hash,但它们没有像类一样的实例方法.例如,函数的一堆选项在哈希中是有意义的; 他们只是松散相关.功能所需的姓名,电子邮件和电话号码可以打包在一起StructOpenStruct.如果该名称,电子邮件和电话号码需要以"First Last"和"Last,First"格式提供名称的方法,那么您应该创建一个类来处理它.

  • "但他们没有像班级那样的实例方法".好吧,有一种非常常见的模式可以将它用作"普通类":`class Point <Struct.new(:x,:y); 方法在这里; end` (48认同)
  • @tokland和今天一样,使用方法自定义struct的"首选"方法是将块传递给构造函数`Point = Struct.new(:x,:y){methods here}`.([来源](http://ruby-doc.org/core-2.3.0/Struct.html#method-c-new))当然,`{...}`可以写成多个行块(`do ... end`),我认为,这是首选方式. (10认同)
  • 一个问题:一旦你到达想要在你的结构中添加方法的那一刻,为什么不使用一个类呢? (4认同)

Rob*_*mme 82

其他基准:

require 'benchmark'
require 'ostruct'

REP = 100000

User = Struct.new(:name, :age)

USER = "User".freeze
AGE = 21
HASH = {:name => USER, :age => AGE}.freeze

Benchmark.bm 20 do |x|
  x.report 'OpenStruct slow' do
    REP.times do |index|
       OpenStruct.new(:name => "User", :age => 21)
    end
  end

  x.report 'OpenStruct fast' do
    REP.times do |index|
       OpenStruct.new(HASH)
    end
  end

  x.report 'Struct slow' do
    REP.times do |index|
       User.new("User", 21)
    end
  end

  x.report 'Struct fast' do
    REP.times do |index|
       User.new(USER, AGE)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

对于想要了解基准测试结果的不耐烦的人,如果不自行运行,这里是上面代码的输出(在MB Pro 2.4GHz i7上)

                          user     system      total        real
OpenStruct slow       4.430000   0.250000   4.680000 (  4.683851)
OpenStruct fast       4.380000   0.270000   4.650000 (  4.649809)
Struct slow           0.090000   0.000000   0.090000 (  0.094136)
Struct fast           0.080000   0.000000   0.080000 (  0.078940)
Run Code Online (Sandbox Code Playgroud)

  • 与ruby 2.14相比,OpenStruct的差异小于0.94-0.97,而Ostruct(MB Pro 2.2Ghz i7)则为0.02-0.03 (5认同)

Til*_*ilo 57

更新:

从Ruby 2.4.1开始,OpenStruct和Struct的速度更加接近.请参阅/sf/answers/3079149111/

先前:

为了完整性:Struct vs. Class vs. Hash vs. OpenStruct

在Ruby 1.9.2上运行与burtlo相似的代码,(4核x86_64中的1个,8GB RAM)[编辑表以对齐列]:

creating 1 Mio Structs :         1.43 sec ,  219 MB /  90MB (virt/res)
creating 1 Mio Class instances : 1.43 sec ,  219 MB /  90MB (virt/res)
creating 1 Mio Hashes  :         4.46 sec ,  493 MB / 364MB (virt/res)
creating 1 Mio OpenStructs :   415.13 sec , 2464 MB / 2.3GB (virt/res) # ~100x slower than Hashes
creating 100K OpenStructs :     10.96 sec ,  369 MB / 242MB (virt/res)

OpenStructs是sloooooow内存密集型,并且不适合大型数据集

创建1个神达OpenStructs是〜100倍慢于创造1个百万哈希值.

start = Time.now

collection = (1..10**6).collect do |i|
  {:name => "User" , :age => 21}
end; 1

stop = Time.now

puts "#{stop - start} seconds elapsed"
Run Code Online (Sandbox Code Playgroud)

  • 从Ruby 2.4.1开始,OpenStruct和Struct的速度更加接近.请参见http://stackoverflow.com/a/43987844/128421 (2认同)

小智 33

两者的用例完全不同.

您可以将Ruby 1.9中的Struct类视为structC语句中的声明.在Ruby中,Struct.new将一组字段名称作为参数并返回一个新类.类似地,在C中,struct声明采用一组字段,并允许程序员使用新的复杂类型,就像使用任何内置类型一样.

红宝石:

Newtype = Struct.new(:data1, :data2)
n = Newtype.new
Run Code Online (Sandbox Code Playgroud)

C:

typedef struct {
  int data1;
  char data2;
} newtype;

newtype n;
Run Code Online (Sandbox Code Playgroud)

可以将OpenStruct类与C中的匿名结构声明进行比较.它允许程序员创建复杂类型的实例.

红宝石:

o = OpenStruct.new(data1: 0, data2: 0) 
o.data1 = 1
o.data2 = 2
Run Code Online (Sandbox Code Playgroud)

C:

struct {
  int data1;
  char data2;
} o;

o.data1 = 1;
o.data2 = 2;
Run Code Online (Sandbox Code Playgroud)

以下是一些常见用例.

OpenStructs可用于轻松地将哈希值转换为响应所有哈希键的一次性对象.

h = { a: 1, b: 2 }
o = OpenStruct.new(h)
o.a = 1
o.b = 2
Run Code Online (Sandbox Code Playgroud)

结构对于速记类定义很有用.

class MyClass < Struct.new(:a,:b,:c)
end

m = MyClass.new
m.a = 1
Run Code Online (Sandbox Code Playgroud)

  • 这是他们之间概念差异的一个很好的答案.感谢您指出OpenStruct的匿名性,我觉得这样做更加清晰. (3认同)

bur*_*tlo 24

与Structs相比,OpenStructs使用更多内存并且表现更慢.

require 'ostruct' 

collection = (1..100000).collect do |index|
   OpenStruct.new(:name => "User", :age => 21)
end
Run Code Online (Sandbox Code Playgroud)

在我的系统上,以下代码在14秒内执行并消耗了1.5 GB的内存.您的里程可能有所不同

User = Struct.new(:name, :age)

collection = (1..100000).collect do |index|
   User.new("User",21)
end
Run Code Online (Sandbox Code Playgroud)

它几乎瞬间完成并消耗了26.6 MB的内存.

  • 但是你知道OpenStruct测试会产生很多临时哈希值.我建议稍微修改基准 - 它仍然支持你的判决(见下文). (3认同)

Dor*_*ian 6

Struct

>> s = Struct.new(:a, :b).new(1, 2)
=> #<struct a=1, b=2>
>> s.a
=> 1
>> s.b
=> 2
>> s.c
NoMethodError: undefined method `c` for #<struct a=1, b=2>
Run Code Online (Sandbox Code Playgroud)

OpenStruct

>> require 'ostruct'
=> true
>> os = OpenStruct.new(a: 1, b: 2)
=> #<OpenStruct a=1, b=2>
>> os.a
=> 1
>> os.b
=> 2
>> os.c
=> nil
Run Code Online (Sandbox Code Playgroud)


Oma*_*shi 5

查看有关新方法的API.那里可以找到很多不同之处.

就个人而言,我非常喜欢OpenStruct,因为我不必事先定义对象的结构,只需添加我想要的东西.我想这将是它的主要(dis)优势?