在泛型Typescript类中实现单例

时间:2015-12-11 17:48:00

标签: generics design-patterns typescript singleton

我有一个泛型类型,它将成为Event系统中事件的基类。我们希望这些是单例,但它也是通用的,这样子类可以在事件触发时指定它们在回调中所期望的参数类型。

我看起来像这样:

export interface IEvent { }
export interfact IEventArg { }

export abstract class Event<TEvent extends IEvent, TEventArg extends IEventArg> implements IMessageHandler {

private static s_instance : any;
private static s_isInitializing : boolean;

constructor() {
    if (!Event.s_isInitializing) 
        throw new Error ("Use the GetInstance method to get this class instance.");
}

public static GetInstance<TEvent extends IEvent, TEventArg extends IEventArg>(type: { new() : T; }) : TEvent {
    if (!s_instance) {
        s_isInitializing = true;
        s_instance = new type();
        s_isInitializing = false;
    }

    return s_instance;
}
}

export class SelectionEvent extends Event<SelectionEvent, EmptyEventArg> {
...
}

var event = SelectionEvent.GetInstance(SelectionEvent);

这里有一些我觉得奇怪的东西,主要是因为Typescript语言要求。 GetInstance的参数似乎是必要的,因为TypeScript似乎不允许var x = new TEvent()。空接口有助于强制执行可以作为泛型类型接受的内容。

这里似乎不起作用的是静态变量和泛型类型的组合。 GetInstance方法中的块是我想要做的,但显然它不能编译。我还担心每个事件不会创建一次静态实例变量,而是每个事件实例只创建一次,因此会被覆盖。

非常感谢此处的任何指导。

1 个答案:

答案 0 :(得分:2)

在C#中,泛型类的每个实例化(类型参数集)都有自己的一组静态成员。这意味着您可以编写单例模式,其中Foo<int>有一个实例,Foo<string>有另一个实例,依此类推。这是可能的,因为泛型在运行时是 manifest - 运行时系统实际上知道什么是泛型,并且可以为每个类型实例分配新对象。另外,类的静态方面可以引用该类的类型参数(这是运行时语义的结果)。

在TypeScript中,情况并非如此。每个类只有一个构造函数,通用或其他。这意味着班级的静态方面不能看到&#34;泛型类型参数,因为对于具有静态成员X<T>的任何类yX.y只有一个运行时槽。

类的单例模式通常不适合TypeScript;见How to define Singleton in TypeScript。我认为你有一些XY problem在这里,所以我不能推荐任何具体的替代方案,而没有更多关于你的用例是什么的信息(可能会发布一个单独的问题)。

此外,你应该永远在TypeScript中有空接口。类型比较结构,因此任何两个空类型都是兼容的,您可以将任何内容分配给键入为空接口的变量。