【发布时间】:2014-11-10 11:48:55
【问题描述】:
我见过代码库使用结构来包装类中的属性和行为。 Ruby 类和结构之间有什么区别?什么时候应该使用一个而不是另一个。
【问题讨论】:
标签: ruby
我见过代码库使用结构来包装类中的属性和行为。 Ruby 类和结构之间有什么区别?什么时候应该使用一个而不是另一个。
【问题讨论】:
标签: ruby
来自Struct docs:
Struct 是一种使用访问器方法将多个属性捆绑在一起的便捷方式,无需编写显式类。
Struct 类生成新的子类,这些子类包含一组成员及其值。为每个成员创建一个读取器和写入器方法,类似于 Module#attr_accessor。
所以,如果我想要一个可以访问名称属性(读写)的Person 类,我可以通过声明一个类来实现:
class Person
attr_accessor :name
def initalize(name)
@name = name
end
end
或使用结构:
Person = Struct.new(:name)
在这两种情况下,我都可以运行以下代码:
person = Person.new
person.name = "Name"
#or Person.new("Name")
puts person.name
什么时候用?
正如描述所述,当我们需要一组可访问的属性而无需编写显式类时,我们会使用 Structs。
例如我想要一个点变量来保存 X 和 Y 值:
point = Struct.new(:x, :y).new(20,30)
point.x #=> 20
更多示例:
【讨论】:
Struct 是一个生成类的类。它只是为了节省您的打字时间。
要补充其他答案,有些事情你不能用 Struct 做,有些事情你可以做。
例如,你不能创建没有参数的结构:
Bar = Struct.new
=> ArgumentError: wrong number of arguments (given 0, expected 1+)
Bar = Struct.new(:bar)
bar = Bar.new(nil)
bar.class
=> Bar
但是,一个类可以让你这样做:
class Foo; end
foo = Foo.new
foo.class
=> Foo
您不能为结构参数设置默认值:
Bar = Struct.new(bar: 'default')
=> ArgumentError: unknown keyword: bar
Bar = Struct.new(bar = 'default')
=> NameError: identifier default needs to be constant
但是你可以用一个类来做到这一点,或者传递一个哈希,参数可以是任何顺序,甚至可以丢失:
class Bar
attr_reader :bar, :rab
def initialize(bar: 'default', rab:)
@bar = bar
@rab = rab
end
end
bar = Bar.new(rab: 'mandatory')
bar.rab
=> 'mandatory'
bar.bar
=> 'default'
bar = Bar.new(rab: 'mandatory', bar: 'custom_value')
bar.rab
=> 'mandatory'
bar.bar
=> 'custom_value'
或直接传递值,参数是否应该以相同的顺序给出,默认的总是在最后:
class Bar
attr_reader :rab, :bar
def initialize(rab, bar = 'default')
@rab = rab
@bar = bar
end
end
bar = Bar.new('mandatory')
bar.rab
=> 'mandatory'
bar.bar
=> 'default'
bar = Bar.new('mandatory', 'custom_value')
bar.rab
=> 'mandatory'
bar.bar
=> 'custom_value'
你不能用 Structs 做任何事情,除非你以这种超级详细的方式为你的参数设置默认值:
A = Struct.new(:a, :b, :c) do
def initialize(a:, b: 2, c: 3)
super(a, b, c)
end
end
(示例取自this answer)
您可以在 Struct 中定义方法:
Foo = Struct.new(:foo) do
def method(argument)
# do something with argument
end
end
end
结构可用于创建数据对象,例如答案之一中提到的点示例。
我有时会使用它们以简单的方式在测试中创建假货和模拟。有时 RSpec allow(foo).to receive(:blah) 等可能会有点过于冗长,而使用 Struct 则要简单得多。
【讨论】:
Struct 是用于创建类的 Ruby 简写。在适用的情况下使用 Struct 可以简化您的代码。 https://www.rubytapas.com/2012/11/07/episode-020-struct/
对此进行了很好的讨论【讨论】:
我想@sam_forgot 建议的基准。比较不是很公平。 如今,类和结构都支持关键字参数。对每个使用关键字参数会产生相反的效果,从我的示例结构中可以看出,关键字参数的性能与类没有太大区别。
require 'benchmark'
REP=1000000
SUser = Struct.new(:name, :age)
SUserK = Struct.new(:name, :age, keyword_init: true)
DATA = { name: "Harry", age: 75 }
DATA2 = DATA.values
class CUser
attr_accessor :name, :age
def initialize(name, age)
@name = name
@age = age
end
end
class CUserK
attr_accessor :name, :age
def initialize(name:, age:)
@name = name
@age = age
end
end
Benchmark.bmbm do |x|
x.report 'Struct create and access, without keyword arguments' do
REP.times do
user = SUser.new(DATA)
"#{user.name} - #{user.age}"
end
end
x.report 'Struct create and access, with keyword arguments' do
REP.times do
user = SUserK.new(**DATA)
"#{user.name} - #{user.age}"
end
end
x.report 'Class create and access, without keyword arguments' do
REP.times do
user = CUser.new(*DATA2)
"#{user.name} - #{user.age}"
end
end
x.report 'Class create and access, with keyword arguments' do
REP.times do
user = CUserK.new(DATA)
"#{user.name} - #{user.age}"
end
end
end
Rehearsal ---------------------------------------------------------------------------------------
Struct create and access, without keyword arguments 3.484609 0.011974 3.496583 ( 3.564523)
Struct create and access, with keyword arguments 0.965959 0.005543 0.971502 ( 1.007738)
Class create and access, without keyword arguments 0.624603 0.003999 0.628602 ( 0.660931)
Class create and access, with keyword arguments 0.901494 0.004926 0.906420 ( 0.952149)
------------------------------------------------------------------------------ total: 6.003107sec
user system total real
Struct create and access, without keyword arguments 3.300488 0.010372 3.310860 ( 3.339511)
Struct create and access, with keyword arguments 0.876742 0.004354 0.881096 ( 0.903551)
Class create and access, without keyword arguments 0.553393 0.003962 0.557355 ( 0.568985)
Class create and access, with keyword arguments 0.831672 0.004811 0.836483 ( 0.850224)
【讨论】:
实际性能差异很大,ruby 2.6.3p62 中的示例行为:
user system total real
Struct create and access 3.052825 0.005204 3.058029 (3.066316)
Class create and access 0.738605 0.001467 0.740072 (0.743738)
示例代码:
require 'benchmark'
REP=1000000
SUser = Struct.new(:name, :age)
DATA = { name: "Harry", age: 75 }
class User
attr_accessor :name, :age
def initialize(name:, age:)
@name = name
@age = age
end
end
Benchmark.bm 20 do |x|
x.report 'Struct create and access' do
REP.times do
user = SUser.new(DATA)
"#{user.name} - #{user.age}"
end
end
x.report 'Class create and access' do
REP.times do
user = User.new(DATA)
"#{user.name} - #{user.age}"
end
end
end
【讨论】: