类 Struct
类 Struct 提供了一种便捷的方式来创建一个简单的类,该类可以存储和获取值。
此示例创建了 Struct
的子类 Struct::Customer
;第一个参数,一个字符串,是子类的名称;其他参数,符号,确定新子类的成员。
Customer = Struct.new('Customer', :name, :address, :zip) Customer.name # => "Struct::Customer" Customer.class # => Class Customer.superclass # => Struct
对应于每个成员,有两个方法,一个写入器和一个读取器,它们存储和获取值
methods = Customer.instance_methods false methods # => [:zip, :address=, :zip=, :address, :name, :name=]
可以通过方法 ::new
创建子类的实例,并为其成员赋值
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe # => #<struct Struct::Customer name="Joe Smith", address="123 Maple, Anytown NC", zip=12345>
成员值可以这样管理
joe.name # => "Joe Smith" joe.name = 'Joseph Smith' joe.name # => "Joseph Smith"
以及这样;请注意,成员名称可以表示为字符串或符号
joe[:name] # => "Joseph Smith" joe[:name] = 'Joseph Smith, Jr.' joe['name'] # => "Joseph Smith, Jr."
参见 Struct::new
.
内容¶ ↑
首先,看看其他地方。类 Struct
-
继承自 类 Object.
-
包含 模块 Enumerable,它提供了数十个其他方法。
另请参见 Data
,它是一个类似但更严格的概念,用于定义不可变值对象。
这里,类 Struct 提供了对以下方面有用的方法
创建 Struct
子类的方法¶ ↑
-
::new
: 返回 Struct 的新子类。
查询方法¶ ↑
比较方法¶ ↑
获取方法¶ ↑
-
[]
: 返回与给定成员名称关联的值。 -
to_a
,values
,deconstruct
: 将self
中的成员值作为数组返回。 -
deconstruct_keys
: 返回给定成员名称的名称/值对的哈希表。 -
dig
: 返回嵌套对象中由给定成员名称和附加参数指定的对象。 -
members
: 返回成员名称的数组。 -
values_at
: 返回包含给定成员名称值的数组。
分配方法¶ ↑
-
[]=
: 将给定值分配给给定成员名称。
迭代方法¶ ↑
转换方法¶ ↑
公共类方法
参见 as_json
.
# File ext/json/lib/json/add/struct.rb, line 9 def self.json_create(object) new(*object['v']) end
如果类使用 keyword_init: true
初始化,则返回 true
。否则返回 nil
或 false
。
示例
Foo = Struct.new(:a) Foo.keyword_init? # => nil Bar = Struct.new(:a, keyword_init: true) Bar.keyword_init? # => true Baz = Struct.new(:a, keyword_init: false) Baz.keyword_init? # => false
static VALUE rb_struct_s_keyword_init_p(VALUE obj) { }
将 Struct
后代的成员名称作为数组返回
Customer = Struct.new(:name, :address, :zip) Customer.members # => [:name, :address, :zip]
static VALUE rb_struct_s_members_m(VALUE klass) { VALUE members = rb_struct_s_members(klass); return rb_ary_dup(members); }
Struct.new
返回一个新的 Struct
子类。
-
该子类可以是匿名的,也可以由
class_name
指定名称。 -
该子类可以拥有由
member_names
指定的成员。 -
该子类可以通过普通参数或关键字参数进行初始化。
该子类拥有自己的 ::new
方法;因此
Foo = Struct.new('Foo', :foo, :bar) # => Struct::Foo f = Foo.new(0, 1) # => #<struct Struct::Foo foo=0, bar=1>
类名
使用字符串参数 class_name
,返回一个名为 Struct::class_name
的新的 Struct
子类。
Foo = Struct.new('Foo', :foo, :bar) # => Struct::Foo Foo.name # => "Struct::Foo" Foo.superclass # => Struct
不使用字符串参数 class_name
,返回一个新的匿名 Struct
子类。
Struct.new(:foo, :bar).name # => nil
代码块
如果提供了代码块,则创建的子类将被传递给该代码块。
Customer = Struct.new('Customer', :name, :address) do |new_class| p "The new subclass is #{new_class}" def greeting "Hello #{name} at #{address}" end end # => Struct::Customer dave = Customer.new('Dave', '123 Main') dave # => #<struct Struct::Customer name="Dave", address="123 Main"> dave.greeting # => "Hello Dave at 123 Main"
Struct.new
的输出
"The new subclass is Struct::Customer"
成员名称
Symbol
参数 member_names
决定了新子类的成员。
Struct.new(:foo, :bar).members # => [:foo, :bar] Struct.new('Foo', :foo, :bar).members # => [:foo, :bar]
该子类拥有与 member_names
相对应的实例方法。
Foo = Struct.new('Foo', :foo, :bar) Foo.instance_methods(false) # => [:foo, :bar, :foo=, :bar=] f = Foo.new # => #<struct Struct::Foo foo=nil, bar=nil> f.foo # => nil f.foo = 0 # => 0 f.bar # => nil f.bar = 1 # => 1 f # => #<struct Struct::Foo foo=0, bar=1>
单例方法
由 Struct.new
返回的子类拥有以下单例方法。
-
::new
方法创建子类的实例。Foo.new # => #<struct Struct::Foo foo=nil, bar=nil> Foo.new(0) # => #<struct Struct::Foo foo=0, bar=nil> Foo.new(0, 1) # => #<struct Struct::Foo foo=0, bar=1> Foo.new(0, 1, 2) # Raises ArgumentError: struct size differs # Initialization with keyword arguments: Foo.new(foo: 0) # => #<struct Struct::Foo foo=0, bar=nil> Foo.new(foo: 0, bar: 1) # => #<struct Struct::Foo foo=0, bar=1> Foo.new(foo: 0, bar: 1, baz: 2) # Raises ArgumentError: unknown keywords: baz
-
:inspect
方法返回子类的字符串表示。Foo.inspect # => "Struct::Foo"
-
::members
方法返回成员名称的数组。Foo.members # => [:foo, :bar]
关键字参数
默认情况下,用于初始化新子类实例的参数可以是位置参数和关键字参数。
可选的关键字参数 keyword_init:
允许强制只接受一种类型的参数。
KeywordsOnly = Struct.new(:foo, :bar, keyword_init: true) KeywordsOnly.new(bar: 1, foo: 0) # => #<struct KeywordsOnly foo=0, bar=1> KeywordsOnly.new(0, 1) # Raises ArgumentError: wrong number of arguments PositionalOnly = Struct.new(:foo, :bar, keyword_init: false) PositionalOnly.new(0, 1) # => #<struct PositionalOnly foo=0, bar=1> PositionalOnly.new(bar: 1, foo: 0) # => #<struct PositionalOnly foo={:foo=>1, :bar=>2}, bar=nil> # Note that no error is raised, but arguments treated as one hash value # Same as not providing keyword_init: Any = Struct.new(:foo, :bar, keyword_init: nil) Any.new(foo: 1, bar: 2) # => #<struct Any foo=1, bar=2> Any.new(1, 2) # => #<struct Any foo=1, bar=2>
static VALUE rb_struct_s_def(int argc, VALUE *argv, VALUE klass) { VALUE name = Qnil, rest, keyword_init = Qnil; long i; VALUE st; VALUE opt; argc = rb_scan_args(argc, argv, "0*:", NULL, &opt); if (argc >= 1 && !SYMBOL_P(argv[0])) { name = argv[0]; --argc; ++argv; } if (!NIL_P(opt)) { static ID keyword_ids[1]; if (!keyword_ids[0]) { keyword_ids[0] = rb_intern("keyword_init"); } rb_get_kwargs(opt, keyword_ids, 0, 1, &keyword_init); if (UNDEF_P(keyword_init)) { keyword_init = Qnil; } else if (RTEST(keyword_init)) { keyword_init = Qtrue; } } rest = rb_ident_hash_new(); RBASIC_CLEAR_CLASS(rest); for (i=0; i<argc; i++) { VALUE mem = rb_to_symbol(argv[i]); if (rb_is_attrset_sym(mem)) { rb_raise(rb_eArgError, "invalid struct member: %"PRIsVALUE, mem); } if (RTEST(rb_hash_has_key(rest, mem))) { rb_raise(rb_eArgError, "duplicate member: %"PRIsVALUE, mem); } rb_hash_aset(rest, mem, Qtrue); } rest = rb_hash_keys(rest); RBASIC_CLEAR_CLASS(rest); OBJ_FREEZE_RAW(rest); if (NIL_P(name)) { st = anonymous_struct(klass); } else { st = new_struct(name, klass); } setup_struct(st, rest); rb_ivar_set(st, id_keyword_init, keyword_init); if (rb_block_given_p()) { rb_mod_module_eval(0, 0, st); } return st; }
公共实例方法
如果以下条件都为真,则返回 true
;否则返回 false
。
-
other.class == self.class
. -
对于每个成员名称
name
,other.name == self.name
。
示例
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe_jr == joe # => true joe_jr[:name] = 'Joe Smith, Jr.' # => "Joe Smith, Jr." joe_jr == joe # => false
static VALUE rb_struct_equal(VALUE s, VALUE s2) { if (s == s2) return Qtrue; if (!RB_TYPE_P(s2, T_STRUCT)) return Qfalse; if (rb_obj_class(s) != rb_obj_class(s2)) return Qfalse; if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { rb_bug("inconsistent struct"); /* should never happen */ } return rb_exec_recursive_paired(recursive_equal, s, s2, s2); }
从 self
返回一个值。
使用符号或字符串参数 name
,返回指定成员的值。
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe[:zip] # => 12345
如果 name
不是成员名称,则引发 NameError
。
使用整数参数 n
,如果 n
在范围内,则返回 self.values[n]
;参见 Array
中的数组索引
joe[2] # => 12345 joe[-2] # => "123 Maple, Anytown NC"
如果 n
超出范围,则引发 IndexError
。
VALUE rb_struct_aref(VALUE s, VALUE idx) { int i = rb_struct_pos(s, &idx); if (i < 0) invalid_struct_pos(s, idx); return RSTRUCT_GET(s, i); }
为成员分配一个值。
如果给定符号或字符串参数 name
,则将给定的 value
分配给名为 name
的成员;返回 value
。
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe[:zip] = 54321 # => 54321 joe # => #<struct Customer name="Joe Smith", address="123 Maple, Anytown NC", zip=54321>
如果 name
不是成员名称,则引发 NameError
。
如果给定整数参数 n
,则将给定的 value
分配给第 n
个成员(如果 n
在范围内);请参见 Array
中的数组索引
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe[2] = 54321 # => 54321 joe[-3] = 'Joseph Smith' # => "Joseph Smith" joe # => #<struct Customer name="Joseph Smith", address="123 Maple, Anytown NC", zip=54321>
如果 n
超出范围,则引发 IndexError
。
VALUE rb_struct_aset(VALUE s, VALUE idx, VALUE val) { int i = rb_struct_pos(s, &idx); if (i < 0) invalid_struct_pos(s, idx); rb_struct_modify(s); RSTRUCT_SET(s, i, val); return val; }
方法 Struct#as_json
和 Struct.json_create
可用于序列化和反序列化 Struct 对象;请参见 Marshal
。
方法 Struct#as_json
序列化 self
,返回一个表示 self
的包含两个元素的哈希。
require 'json/add/struct' Customer = Struct.new('Customer', :name, :address, :zip) x = Struct::Customer.new.as_json # => {"json_class"=>"Struct::Customer", "v"=>[nil, nil, nil]}
方法 JSON.create
反序列化此类哈希,返回一个 Struct 对象。
Struct::Customer.json_create(x) # => #<struct Struct::Customer name=nil, address=nil, zip=nil>
# File ext/json/lib/json/add/struct.rb, line 30 def as_json(*) klass = self.class.name klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!" { JSON.create_id => klass, 'v' => values, } end
返回给定成员名称的名称/值对的哈希。
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) h = joe.deconstruct_keys([:zip, :address]) h # => {:zip=>12345, :address=>"123 Maple, Anytown NC"}
如果 array_of_names
为 nil
,则返回所有名称和值。
h = joe.deconstruct_keys(nil) h # => {:name=>"Joseph Smith, Jr.", :address=>"123 Maple, Anytown NC", :zip=>12345}
static VALUE rb_struct_deconstruct_keys(VALUE s, VALUE keys) { VALUE h; long i; if (NIL_P(keys)) { return rb_struct_to_h(s); } if (UNLIKELY(!RB_TYPE_P(keys, T_ARRAY))) { rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array or nil)", rb_obj_class(keys)); } if (RSTRUCT_LEN(s) < RARRAY_LEN(keys)) { return rb_hash_new_with_size(0); } h = rb_hash_new_with_size(RARRAY_LEN(keys)); for (i=0; i<RARRAY_LEN(keys); i++) { VALUE key = RARRAY_AREF(keys, i); int i = rb_struct_pos(s, &key); if (i < 0) { return h; } rb_hash_aset(h, key, RSTRUCT_GET(s, i)); } return h; }
在嵌套对象中查找并返回一个对象。嵌套对象可以是各种类的实例。请参见 Dig 方法。
如果给定符号或字符串参数 name
,则返回由 name
和 identifiers
指定的对象。
Foo = Struct.new(:a) f = Foo.new(Foo.new({b: [1, 2, 3]})) f.dig(:a) # => #<struct Foo a={:b=>[1, 2, 3]}> f.dig(:a, :a) # => {:b=>[1, 2, 3]} f.dig(:a, :a, :b) # => [1, 2, 3] f.dig(:a, :a, :b, 0) # => 1 f.dig(:b, 0) # => nil
如果给定整数参数 n
,则返回由 n
和 identifiers
指定的对象。
f.dig(0) # => #<struct Foo a={:b=>[1, 2, 3]}> f.dig(0, 0) # => {:b=>[1, 2, 3]} f.dig(0, 0, :b) # => [1, 2, 3] f.dig(0, 0, :b, 0) # => 1 f.dig(:b, 0) # => nil
static VALUE rb_struct_dig(int argc, VALUE *argv, VALUE self) { rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS); self = rb_struct_lookup(self, *argv); if (!--argc) return self; ++argv; return rb_obj_dig(argc, argv, self, Qnil); }
使用每个成员的值调用给定的块;返回 self
。
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.each {|value| p value }
输出
"Joe Smith" "123 Maple, Anytown NC" 12345
如果没有给出块,则返回一个 Enumerator
。
相关:each_pair
。
static VALUE rb_struct_each(VALUE s) { long i; RETURN_SIZED_ENUMERATOR(s, 0, 0, struct_enum_size); for (i=0; i<RSTRUCT_LEN(s); i++) { rb_yield(RSTRUCT_GET(s, i)); } return s; }
使用每个成员名称/值对调用给定的块;返回 self
。
Customer = Struct.new(:name, :address, :zip) # => Customer joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.each_pair {|(name, value)| p "#{name} => #{value}" }
输出
"name => Joe Smith" "address => 123 Maple, Anytown NC" "zip => 12345"
如果没有给出块,则返回一个 Enumerator
。
相关:each
.
static VALUE rb_struct_each_pair(VALUE s) { VALUE members; long i; RETURN_SIZED_ENUMERATOR(s, 0, 0, struct_enum_size); members = rb_struct_members(s); if (rb_block_pair_yield_optimizable()) { for (i=0; i<RSTRUCT_LEN(s); i++) { VALUE key = rb_ary_entry(members, i); VALUE value = RSTRUCT_GET(s, i); rb_yield_values(2, key, value); } } else { for (i=0; i<RSTRUCT_LEN(s); i++) { VALUE key = rb_ary_entry(members, i); VALUE value = RSTRUCT_GET(s, i); rb_yield(rb_assoc_new(key, value)); } } return s; }
如果以下条件都为真,则返回 true
;否则返回 false
。
-
other.class == self.class
. -
对于每个成员名称
name
,other.name.eql?(self.name)
。Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe_jr.eql?(joe) # => true joe_jr[:name] = 'Joe Smith, Jr.' joe_jr.eql?(joe) # => false
相关:Object#==
.
static VALUE rb_struct_eql(VALUE s, VALUE s2) { if (s == s2) return Qtrue; if (!RB_TYPE_P(s2, T_STRUCT)) return Qfalse; if (rb_obj_class(s) != rb_obj_class(s2)) return Qfalse; if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { rb_bug("inconsistent struct"); /* should never happen */ } return rb_exec_recursive_paired(recursive_eql, s, s2, s2); }
返回 self
的整数哈希值。
两个相同类且内容相同的结构体将具有相同的哈希码(并且将使用 Struct#eql?
进行比较)。
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.hash == joe_jr.hash # => true joe_jr[:name] = 'Joe Smith, Jr.' joe.hash == joe_jr.hash # => false
相关:Object#hash
.
static VALUE rb_struct_hash(VALUE s) { long i, len; st_index_t h; VALUE n; h = rb_hash_start(rb_hash(rb_obj_class(s))); len = RSTRUCT_LEN(s); for (i = 0; i < len; i++) { n = rb_hash(RSTRUCT_GET(s, i)); h = rb_hash_uint(h, NUM2LONG(n)); } h = rb_hash_end(h); return ST2FIX(h); }
返回 self
的字符串表示。
Customer = Struct.new(:name, :address, :zip) # => Customer joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.inspect # => "#<struct Customer name=\"Joe Smith\", address=\"123 Maple, Anytown NC\", zip=12345>"
static VALUE rb_struct_inspect(VALUE s) { return rb_exec_recursive(inspect_struct, s, rb_str_new2("#<struct ")); }
将 self
中的成员名称作为数组返回。
Customer = Struct.new(:name, :address, :zip) Customer.new.members # => [:name, :address, :zip]
相关:to_a
.
static VALUE rb_struct_members_m(VALUE obj) { return rb_struct_s_members_m(rb_obj_class(obj)); }
如果给定块,则返回 self
中块返回真值的值的数组。
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) a = joe.select {|value| value.is_a?(String) } a # => ["Joe Smith", "123 Maple, Anytown NC"] a = joe.select {|value| value.is_a?(Integer) } a # => [12345]
如果没有给定块,则返回一个 Enumerator
.
static VALUE rb_struct_select(int argc, VALUE *argv, VALUE s) { VALUE result; long i; rb_check_arity(argc, 0, 0); RETURN_SIZED_ENUMERATOR(s, 0, 0, struct_enum_size); result = rb_ary_new(); for (i = 0; i < RSTRUCT_LEN(s); i++) { if (RTEST(rb_yield(RSTRUCT_GET(s, i)))) { rb_ary_push(result, RSTRUCT_GET(s, i)); } } return result; }
返回成员数量。
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.size #=> 3
VALUE rb_struct_size(VALUE s) { return LONG2FIX(RSTRUCT_LEN(s)); }
将 self
中的值作为数组返回。
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.to_a # => ["Joe Smith", "123 Maple, Anytown NC", 12345]
相关:members
.
static VALUE rb_struct_to_a(VALUE s) { return rb_ary_new4(RSTRUCT_LEN(s), RSTRUCT_CONST_PTR(s)); }
返回一个包含每个成员的名称和值的哈希表。
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) h = joe.to_h h # => {:name=>"Joe Smith", :address=>"123 Maple, Anytown NC", :zip=>12345}
如果给定一个代码块,它将使用每个名称/值对进行调用;代码块应返回一个包含两个元素的数组,其元素将成为返回的哈希中的键/值对。
h = joe.to_h{|name, value| [name.upcase, value.to_s.upcase]} h # => {:NAME=>"JOE SMITH", :ADDRESS=>"123 MAPLE, ANYTOWN NC", :ZIP=>"12345"}
如果代码块返回不合适的值,则会引发 ArgumentError
。
static VALUE rb_struct_to_h(VALUE s) { VALUE h = rb_hash_new_with_size(RSTRUCT_LEN(s)); VALUE members = rb_struct_members(s); long i; int block_given = rb_block_given_p(); for (i=0; i<RSTRUCT_LEN(s); i++) { VALUE k = rb_ary_entry(members, i), v = RSTRUCT_GET(s, i); if (block_given) rb_hash_set_pair(h, rb_yield_values(2, k, v)); else rb_hash_aset(h, k, v); } return h; }
返回一个表示 self
的 JSON
字符串。
require 'json/add/struct' Customer = Struct.new('Customer', :name, :address, :zip) puts Struct::Customer.new.to_json
输出
{"json_class":"Struct","t":{'name':'Rowdy',"age":null}}
# File ext/json/lib/json/add/struct.rb, line 49 def to_json(*args) as_json.to_json(*args) end
返回来自 self
的值数组。
如果给定整数参数 integers
,则返回一个包含由 integers
中的任何一个给出的每个值的数组。
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.values_at(0, 2) # => ["Joe Smith", 12345] joe.values_at(2, 0) # => [12345, "Joe Smith"] joe.values_at(2, 1, 0) # => [12345, "123 Maple, Anytown NC", "Joe Smith"] joe.values_at(0, -3) # => ["Joe Smith", "Joe Smith"]
如果 integers
中的任何一个超出范围,则会引发 IndexError
;请参阅 Array
中的数组索引。
如果给定整数范围参数 integer_range
,则返回一个包含由范围元素给出的每个值的数组;对于大于结构的范围元素,用 nil
值填充。
joe.values_at(0..2) # => ["Joe Smith", "123 Maple, Anytown NC", 12345] joe.values_at(-3..-1) # => ["Joe Smith", "123 Maple, Anytown NC", 12345] joe.values_at(1..4) # => ["123 Maple, Anytown NC", 12345, nil, nil]
如果范围的任何元素为负数且超出范围,则会引发 RangeError
;请参阅 Array
中的数组索引。
static VALUE rb_struct_values_at(int argc, VALUE *argv, VALUE s) { return rb_get_values_at(s, RSTRUCT_LEN(s), argc, argv, struct_entry); }