ArrayAccess by string on an unknown or dynamic 类型
ArrayAccess by string on an unknown or dynamic type
我使用一些更高级的东西(例如 haxe 中的抽象)构建了自己的 Yaml 解析器,以便更好地理解一切是如何工作的,但我又碰壁了。
重要的部分是摘要,abstract YamlMap (StringMap<Either<String, YamlMap>>)
,它充当我的数据的容器。 "node" 可以是字符串或另一个 yamlmap,位于树的更深处。
这个 YamlMap
有多种获取数据的方法,例如 getMap(key):YamlMap
和 getString(key):String
,以及一个动态访问方法 getDynamic(key):Dynamic
。
不幸的是,似乎只有一个 @:arrayAccess
可以工作是抽象的,或者我遗漏了一些东西。似乎你不能 "arrayAccess" 带有字符串的动态对象,或者至少编译器阻止我这样做。
所以,这有效:data.getMap('test_node').getMap('sub_node1').getString('value2')
但这不是:data['test_node']['sub_node2']['value2']
如果我将 getDynamic
设置为 arrayAccess,它告诉我 sub_node2
应该是一个整数。但是,如果我将 getMap
和 getString
都设置为 arrayAccess,它将始终调用第一个 arrayAccess 标记的方法。所以它要么在尝试获取 "value" 时失败(这是一个字符串,但代码试图获取一个映射)或者不编译,因为我猜,它试图从字符串中访问一个字符而不是地图位置。
所以,我的猜测是,与此相关manual entry,对任何非抽象对象的 arrayAccess 都被锁定为 int,因此拒绝使用字符串访问动态对象。
我能想到的一个可能的解决方案是 return 某种抽象,它将 "flatten" 转换为正确的类型,而不是使用动态值。是否有任何其他方法可以在动态地图上实现字符串化数组访问?
注意:在某种程度上,这是出于好奇,因为对映射和字符串进行不同调用的当前方法足以满足日常使用。我也知道现有的 yaml haxelib,但这是一次学习经验,因为它试图取代有时可能有问题的 haxelib。
这里有一个 Pastebin YamlMap 摘要供感兴趣的人使用。
首先,一个抽象 可以 有多个 @:arrayAccess
方法:
abstract MultiArrayAccess({}) from {} {
public function new() this = {};
@:arrayAccess function getInt(i:Int) return i;
@:arrayAccess function getString(s:String) return Std.parseInt(s);
}
var i = new MultiArrayAccess();
trace(i[1]); // 1
trace(i["3"]); // 3
这与您的示例之间的区别在于键类型不同。这是必要的,因为对抽象(和一般抽象)的数组访问是一个 编译时 特性。这两个数组访问的 AST 转储如下所示:
_Main.MultiArrayAccess_Impl_.getInt(i, 1)
_Main.MultiArrayAccess_Impl_.getString(i, "3")
因此,编译器必须知道在编译时调用哪个方法。这在您的情况下是不可能的,因为两种方法的键类型都是 String
。通过将 getString()
和 getMap()
替换为 []
,您正在丢失信息。
It also seems that you cannot "arrayAccess" a dynamic object with strings, or at least the compiler is preventing me from doing it.
没错,Dynamic
上不允许数组访问。但是,标准库有一个抽象叫haxe.DynamicAccess
,是用Reflect.field()
实现数组访问的抽象。当然,这会导致运行时开销。您可以有一个完全动态的实现基于:
typedef YamlMap = haxe.DynamicAccess<YamlMapValue>;
abstract YamlMapValue(YamlMap) from YamlMap {
@:arrayAccess function get(key:String):YamlMapValue {
return this.get(key);
}
@:arrayAccess function setMap(key:String, value:YamlMap) {
Reflect.setField(this, key, value);
}
@:arrayAccess function setString(key:String, value:String) {
Reflect.setField(this, key, value);
}
}
var data = new YamlMap();
data["test_node"] = new YamlMap();
data["test_node"]["sub_node1"] = new YamlMap();
data["test_node"]["sub_node1"]["value2"] = "foo";
var map:YamlMapValue = data["test_node"]["sub_node1"];
trace(map["value2"]); // foo
但是,这不一定是个好主意,因为这样会损失很多类型安全性(以及性能)。
我使用一些更高级的东西(例如 haxe 中的抽象)构建了自己的 Yaml 解析器,以便更好地理解一切是如何工作的,但我又碰壁了。
重要的部分是摘要,abstract YamlMap (StringMap<Either<String, YamlMap>>)
,它充当我的数据的容器。 "node" 可以是字符串或另一个 yamlmap,位于树的更深处。
这个 YamlMap
有多种获取数据的方法,例如 getMap(key):YamlMap
和 getString(key):String
,以及一个动态访问方法 getDynamic(key):Dynamic
。
不幸的是,似乎只有一个 @:arrayAccess
可以工作是抽象的,或者我遗漏了一些东西。似乎你不能 "arrayAccess" 带有字符串的动态对象,或者至少编译器阻止我这样做。
所以,这有效:data.getMap('test_node').getMap('sub_node1').getString('value2')
但这不是:data['test_node']['sub_node2']['value2']
如果我将 getDynamic
设置为 arrayAccess,它告诉我 sub_node2
应该是一个整数。但是,如果我将 getMap
和 getString
都设置为 arrayAccess,它将始终调用第一个 arrayAccess 标记的方法。所以它要么在尝试获取 "value" 时失败(这是一个字符串,但代码试图获取一个映射)或者不编译,因为我猜,它试图从字符串中访问一个字符而不是地图位置。
所以,我的猜测是,与此相关manual entry,对任何非抽象对象的 arrayAccess 都被锁定为 int,因此拒绝使用字符串访问动态对象。
我能想到的一个可能的解决方案是 return 某种抽象,它将 "flatten" 转换为正确的类型,而不是使用动态值。是否有任何其他方法可以在动态地图上实现字符串化数组访问?
注意:在某种程度上,这是出于好奇,因为对映射和字符串进行不同调用的当前方法足以满足日常使用。我也知道现有的 yaml haxelib,但这是一次学习经验,因为它试图取代有时可能有问题的 haxelib。
这里有一个 Pastebin YamlMap 摘要供感兴趣的人使用。
首先,一个抽象 可以 有多个 @:arrayAccess
方法:
abstract MultiArrayAccess({}) from {} {
public function new() this = {};
@:arrayAccess function getInt(i:Int) return i;
@:arrayAccess function getString(s:String) return Std.parseInt(s);
}
var i = new MultiArrayAccess();
trace(i[1]); // 1
trace(i["3"]); // 3
这与您的示例之间的区别在于键类型不同。这是必要的,因为对抽象(和一般抽象)的数组访问是一个 编译时 特性。这两个数组访问的 AST 转储如下所示:
_Main.MultiArrayAccess_Impl_.getInt(i, 1)
_Main.MultiArrayAccess_Impl_.getString(i, "3")
因此,编译器必须知道在编译时调用哪个方法。这在您的情况下是不可能的,因为两种方法的键类型都是 String
。通过将 getString()
和 getMap()
替换为 []
,您正在丢失信息。
It also seems that you cannot "arrayAccess" a dynamic object with strings, or at least the compiler is preventing me from doing it.
没错,Dynamic
上不允许数组访问。但是,标准库有一个抽象叫haxe.DynamicAccess
,是用Reflect.field()
实现数组访问的抽象。当然,这会导致运行时开销。您可以有一个完全动态的实现基于:
typedef YamlMap = haxe.DynamicAccess<YamlMapValue>;
abstract YamlMapValue(YamlMap) from YamlMap {
@:arrayAccess function get(key:String):YamlMapValue {
return this.get(key);
}
@:arrayAccess function setMap(key:String, value:YamlMap) {
Reflect.setField(this, key, value);
}
@:arrayAccess function setString(key:String, value:String) {
Reflect.setField(this, key, value);
}
}
var data = new YamlMap();
data["test_node"] = new YamlMap();
data["test_node"]["sub_node1"] = new YamlMap();
data["test_node"]["sub_node1"]["value2"] = "foo";
var map:YamlMapValue = data["test_node"]["sub_node1"];
trace(map["value2"]); // foo
但是,这不一定是个好主意,因为这样会损失很多类型安全性(以及性能)。