这个Dynamic数据类型像C#的动态类型,但是我C#的功力很弱,算新学了。
一、总述
官方教程上说:HAXE3编译器从不将类型推断为动态类型,因此用户必须对其进行明确说明。以前的haxe版本用于推断混合类型的数组,例如 var arr:array<dynamic>=[1,true,“foo”]。HAXE3之后就不能用了。
动态类型使用应该尽量少使用,效率不高,除非迫不得已。HAXE反射时候会使用动态类型,有时它是处理编译时未知的自定义数据结构时的最佳选择。如下面:
class Main {
static function main() {
var jsonData = '[1, 2, 3]';
var json = haxe.Json.parse(jsonData);
$type(json); // Unknown<0>
for (i in 0...json.length) { //可以用这种方式完成经典for循环
// Array access is not allowed on
// {+ length : Int }
trace(json[0]);
}
}
}
这段代码是无法运行的,错误在于json[0]不能访问,因为var json 没有具体说明它的类型,因此haxe会推断parse的结果为一个匿名结构体,所以对于匿名结构体,则没有下表访问属性。因此要显式申明变量 json:Dynamic,但显式申明后,则没有了length属性了。
二、动态接口的实现
类可以实现Dynamic或Dynamic<T>,在前一种情况下,字段可以有任何类型,在后一种情况下,字段被约束为与参数类型兼容。看官方的例子:
class ImplementsDynamic
implements Dynamic<String> {
public var present:Int;
public function new() {}
}
class Main {
static public function main() {
var c = new ImplementsDynamic();
c.present = 1;//这是允许的,因为present是已经存在的
// valid, assigned value is a String
c.stringField = "foo";//这是可以的,虽然没有指定该字段,但是可以类似js一样,动态分配该字段
// error, Int should be String
c.intField = 1;//不能这么做,因为后增补的因为实现了Dynamaic<String>,所以不能
}
}
实现动态(有或没有类型参数)的类也可以使用名为resolve的特殊方法。如果进行了读取访问,并且相关字段不存在,则使用字段名作为参数调用resolve方法:
class Resolve implements Dynamic<String> {
public var present:Int;
public function new() {}
//这个函数是回调函数,如果发现不存在的字段则自动调用
function resolve(field:String) {
return "Tried to resolve " +field;
}
}
class Main {
static public function main() {
var c = new Resolve();
c.present = 2;
c.hello = "test";
trace(c.present);
trace(c.hello);
trace(c.resolveMe);
}
}
三、DynamicAccess
是一种抽象类型,用于处理匿名结构体,匿名结构体旨在通过字符串键保存对象集合。官方例子
class Main {
static public function main() {
var user:haxe.DynamicAccess<Dynamic> = {};
// Sets values for specified keys.
user.set("name", "Mark");
user.set("age", 25);
// Returns values by specified keys.
trace(user.get("name")); // "Mark"
trace(user.get("age")); // 25
// Tells if the structure contains a specified key
trace(user.exists("name")); // true
}
}
如果我把haxe.DynamicAccess<Dynamic>去掉的话,则程序报错。在这点上我觉得不如其他动态语言,自动推断的能力有待提高。
四、Any类型
Any是一种兼容其他的数据类型。它有一个用途——保存任何类型的值,但要实际使用这些值,需要显式转换。
class Main {
static function setAnyValue(value:Any) {
trace(value);
}
static function getAnyValue():Any {
return 42;
}
static function main() {
// value of any type works
setAnyValue("someValue");
setAnyValue(42);
var value = getAnyValue();
$type(value); // Any, not Unknown<0>
// won't compile: no dynamic field access
// value.charCodeAt(0);
/**
public static function is( v : Dynamic, t : Dynamic ) : Bool; is方法是这么申明的,用来判断类型是否一致
**/
if (Std.is(value, String)) {
// explicit promotion, type-safe
trace((value : String).charCodeAt(0));//强制转型
}
}
}
它是动态的更类型安全的替代方案,因为它不支持字段访问或运算符,而且它绑定到单变形。因此,要使用实际值,需要将其显式升级为另一种类型。