如何在泛型函数中转换为动态类型?
How to cast to dynamic type at generic function?
我有一个基本原型class:
class CItemProto {
public var id:Int;
public var count:Int;
...
}
和一些不同类型的扩展:
class CItemThing extends CItemProto { ... }
class CItemResource extends CItemProto { ... }
class CItemRecipe extends CItemProto { ... }
...等等。项目的每个实例都有唯一的 ID,所以我可以通过简单的 IntMap 访问将我所有的东西存储在一个库存中 class:
class CInventory {
var mMap:IntMap<CItemProto>;
public function new() {
mMap = new IntMap();
}
public inline function set(item:CItemProto) { mMap.set(item.id, item); }
public function get<T:CItemProto>(id:Int):T {
var item = mMap.get(aId);
if (Std.is(item, Class<T>)) // it doesn't work saying Unexpected )
return cast item;
return null;
}
}
我的观点是使用 get() 访问器和一些项目 ID 和这个项目类型,如果我在类型选择上有误,方法应该 return null 值。例如:
// should return instance of CItemThing if it exists or null in the other way
var thing:CItemThing = inventory.get(123);
但是没有用。如果我请求错误的类型,简单的不安全转换会失败,安全转换需要 Dynamic 类型而不是通用的 T 替换。我应该怎么做才能按类型过滤请求的项目?因为我可以将类型作为第二个参数传递,但它看起来笨重且过多。
UPDATE 我找到了主题 How to look for a class type in an array using generics 所以我的问题没有意义。我将传递所需的类型作为第二个参数。
我做了一个非常丑陋的解决方法:
typedef Constructible = {
function new():Void;
}
@:generic
public function get<T:(CItemProto,Constructible)>(id:Int):Null<T> {
var typed = new T();
var item:CItemProto = mMap.get(id);
if (Std.is(item, Type.getClass(typed)))
return cast item;
return null;
}
现在我可以请求任何类型的项目,例如:
var thing:CItemThing = inventory.get(111);
var resource:CItemResource = inventory.get(222);
但你不应该这样做。
我会用这样的东西:
public function safeGetAs<T:CItemProto>(id:Int, c:Class<T>):T {
var v = mMap.get(id);
if (Std.is(v, c)) return cast v;
return null;
}
然后你必须显式键入你想要的内容,因为这不是编译时可以解决的问题。打字也更有意义:
inventory.set(new CItemThing(1));
inventory.set(new CItemResource(2));
var a = inventory.safeGetAs(1, CItemThing);
trace(a); // returns instance
$type(a); // expected: CItemThing. is: CItemThing.
var b = inventory.safeGetAs(2, CItemThing);
trace(b); // returns null
$type(b); // expected: CItemThing. is: CItemResource.
我有一个基本原型class:
class CItemProto {
public var id:Int;
public var count:Int;
...
}
和一些不同类型的扩展:
class CItemThing extends CItemProto { ... }
class CItemResource extends CItemProto { ... }
class CItemRecipe extends CItemProto { ... }
...等等。项目的每个实例都有唯一的 ID,所以我可以通过简单的 IntMap 访问将我所有的东西存储在一个库存中 class:
class CInventory {
var mMap:IntMap<CItemProto>;
public function new() {
mMap = new IntMap();
}
public inline function set(item:CItemProto) { mMap.set(item.id, item); }
public function get<T:CItemProto>(id:Int):T {
var item = mMap.get(aId);
if (Std.is(item, Class<T>)) // it doesn't work saying Unexpected )
return cast item;
return null;
}
}
我的观点是使用 get() 访问器和一些项目 ID 和这个项目类型,如果我在类型选择上有误,方法应该 return null 值。例如:
// should return instance of CItemThing if it exists or null in the other way
var thing:CItemThing = inventory.get(123);
但是没有用。如果我请求错误的类型,简单的不安全转换会失败,安全转换需要 Dynamic 类型而不是通用的 T 替换。我应该怎么做才能按类型过滤请求的项目?因为我可以将类型作为第二个参数传递,但它看起来笨重且过多。
UPDATE 我找到了主题 How to look for a class type in an array using generics 所以我的问题没有意义。我将传递所需的类型作为第二个参数。
我做了一个非常丑陋的解决方法:
typedef Constructible = {
function new():Void;
}
@:generic
public function get<T:(CItemProto,Constructible)>(id:Int):Null<T> {
var typed = new T();
var item:CItemProto = mMap.get(id);
if (Std.is(item, Type.getClass(typed)))
return cast item;
return null;
}
现在我可以请求任何类型的项目,例如:
var thing:CItemThing = inventory.get(111);
var resource:CItemResource = inventory.get(222);
但你不应该这样做。
我会用这样的东西:
public function safeGetAs<T:CItemProto>(id:Int, c:Class<T>):T {
var v = mMap.get(id);
if (Std.is(v, c)) return cast v;
return null;
}
然后你必须显式键入你想要的内容,因为这不是编译时可以解决的问题。打字也更有意义:
inventory.set(new CItemThing(1));
inventory.set(new CItemResource(2));
var a = inventory.safeGetAs(1, CItemThing);
trace(a); // returns instance
$type(a); // expected: CItemThing. is: CItemThing.
var b = inventory.safeGetAs(2, CItemThing);
trace(b); // returns null
$type(b); // expected: CItemThing. is: CItemResource.