如何通过符号化名称设置变量
How to set a variable by its symbolized name
我正在学习 crystal(只是为了好玩)并尝试为结构实现某种 []=
方法。这是第一次尝试:
struct Foo
@str : String | Int32 # Have to share all the types, NOT OK
@int : Int32 | String # Have to share all the types, NOT OK
def initialize(@str = "foo", @int = 111)
end
def []=(variable, value)
{% for ivar in @type.instance_vars %}
@{{ivar}} = value if {{ivar.id.symbolize}} == variable
{% end %}
end
end
foo = Foo.new
foo[:str] = "bar" # OK
foo[:int] = 222 # OK
可以,但缺点是所有的实例变量应该共享相同的类型,否则编译器会报错。我的第二次尝试是重新实现这样的方法:
def []=(variable, value)
{% for ivar in @type.instance_vars %}
{% if ivar.id.symbolize == variable %} # ERROR: undefined macro variable 'variable'
@{{ivar}} = value
{% end %}
{% end %}
end
但这不起作用,因为无法解析 {%%}
语法中的 variable
。如何克服这个问题?也许还有其他惯用的方法来实现这种方法?
您不需要实例变量具有所有可能类型的联合,您可以在 setter 方法中限制为匹配类型:
struct Foo
@str : String
@int : Int32
def initialize(@str = "foo", @int = 111)
end
def []=(variable, value)
{% for ivar in @type.instance_vars %}
if {{ivar.id.symbolize}} == variable
if value.is_a?({{ ivar.type.id }})
@{{ivar}} = value
else
raise "Invalid type #{value.class} for {{ivar.id.symbolize}} (expected {{ ivar.type.id }})"
end
end
{% end %}
end
end
foo = Foo.new
foo[:str] = "bar"
foo[:int] = 222
foo[:int] = "string" # => Invalid type String for :int (expected Int32)
虽然这当然是可能的,但缺点是这些类型不匹配只能在运行时检测到。如果您直接访问属性(foo.int = "string"
甚至不会编译),您将获得更好的类型安全性。
应该可以将 []=
作为一个宏来实现,它可能会在编译时抱怨类型不匹配,但我不确定这就是你要找的。
更好的解决方案可能是只使用 NamedTuple
。或者您可能根本不需要使用符号访问字段。我不知道是否有真正的用例。
我正在学习 crystal(只是为了好玩)并尝试为结构实现某种 []=
方法。这是第一次尝试:
struct Foo
@str : String | Int32 # Have to share all the types, NOT OK
@int : Int32 | String # Have to share all the types, NOT OK
def initialize(@str = "foo", @int = 111)
end
def []=(variable, value)
{% for ivar in @type.instance_vars %}
@{{ivar}} = value if {{ivar.id.symbolize}} == variable
{% end %}
end
end
foo = Foo.new
foo[:str] = "bar" # OK
foo[:int] = 222 # OK
可以,但缺点是所有的实例变量应该共享相同的类型,否则编译器会报错。我的第二次尝试是重新实现这样的方法:
def []=(variable, value)
{% for ivar in @type.instance_vars %}
{% if ivar.id.symbolize == variable %} # ERROR: undefined macro variable 'variable'
@{{ivar}} = value
{% end %}
{% end %}
end
但这不起作用,因为无法解析 {%%}
语法中的 variable
。如何克服这个问题?也许还有其他惯用的方法来实现这种方法?
您不需要实例变量具有所有可能类型的联合,您可以在 setter 方法中限制为匹配类型:
struct Foo
@str : String
@int : Int32
def initialize(@str = "foo", @int = 111)
end
def []=(variable, value)
{% for ivar in @type.instance_vars %}
if {{ivar.id.symbolize}} == variable
if value.is_a?({{ ivar.type.id }})
@{{ivar}} = value
else
raise "Invalid type #{value.class} for {{ivar.id.symbolize}} (expected {{ ivar.type.id }})"
end
end
{% end %}
end
end
foo = Foo.new
foo[:str] = "bar"
foo[:int] = 222
foo[:int] = "string" # => Invalid type String for :int (expected Int32)
虽然这当然是可能的,但缺点是这些类型不匹配只能在运行时检测到。如果您直接访问属性(foo.int = "string"
甚至不会编译),您将获得更好的类型安全性。
应该可以将 []=
作为一个宏来实现,它可能会在编译时抱怨类型不匹配,但我不确定这就是你要找的。
更好的解决方案可能是只使用 NamedTuple
。或者您可能根本不需要使用符号访问字段。我不知道是否有真正的用例。