Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 基于无约束泛型类型创建对象实例_Delphi_Generics - Fatal编程技术网

Delphi 基于无约束泛型类型创建对象实例

Delphi 基于无约束泛型类型创建对象实例,delphi,generics,Delphi,Generics,我有一个无约束的泛型原子类型,它实现了一个初始值设定项(详细信息见我的文档) 类型 原子=类 类型TFactory=对函数的引用:T; 类函数初始化(变量存储:T;工厂:TFactory):T; 结束; 现在我想编写一个简化的初始化函数,它将从T获取类型信息(前提是typeof(T)是tkClass)并使用默认构造函数创建新实例(必要时) 可悲的是,这失败了: class function Atomic<T>.Initialize(var storage: T): T; begin

我有一个无约束的泛型原子类型,它实现了一个初始值设定项(详细信息见我的文档)

类型
原子=类
类型TFactory=对函数的引用:T;
类函数初始化(变量存储:T;工厂:TFactory):T;
结束;
现在我想编写一个简化的初始化函数,它将从T获取类型信息(前提是typeof(T)是tkClass)并使用默认构造函数创建新实例(必要时)

可悲的是,这失败了:

class function Atomic<T>.Initialize(var storage: T): T;
begin
  if not assigned(PPointer(@storage)^) then begin
    if PTypeInfo(TypeInfo(T))^.Kind  <> tkClass then
      raise Exception.Create('Atomic<T>.Initialize: Unsupported type');
    Result := Atomic<T>.Initialize(storage,
      function: T
      begin
        Result := TClass(T).Create; // <-- E2571
      end);
  end;
end;
类函数原子初始化(var存储:T):T;
开始
如果未分配(点(@storage)^),则开始
如果PTypeInfo(TypeInfo(T))^.kindtkclass那么
引发异常.Create('Atomic.Initialize:Unsupported type');
结果:=原子。初始化(存储,
功能:T
开始
结果:=TClass(T).Create;//可用于获取类引用:

Result := T(GetTypeData(PTypeInfo(TypeInfo(T)))^.ClassType.Create);
在Delphi XE2中(希望在下一版本中),您可以执行以下操作:

var
  xInValue, xOutValue: TValue;

xInValue := GetTypeData(PTypeInfo(TypeInfo(T)))^.ClassType.Create;
xInValue.TryCast(TypeInfo(T), xOutValue);
Result := xOutValue.AsType<T>;
var
xInValue,xOutValue:TValue;
xInValue:=GetTypeData(PTypeInfo(TypeInfo(T))^.ClassType.Create;
TryCast(TypeInfo(T),xOutValue);
结果:=xOutValue.AsType;

(OmniThreadLibrary论坛中使用的
cjsalamon
发现了这种相当绕开的方式:)

您可以使用新的Delphi
Rtti
来执行此任务。给定解决方案的缺点是,如果构造函数未命名为Create,它将无法工作。如果您需要使它一直工作,只需枚举您的类型方法,检查它是否是构造函数,是否有0个参数,然后调用它。在Delphi XE中工作。示例代码:

class function TTest.CreateInstance<T>: T;
var
  AValue: TValue;
  ctx: TRttiContext;
  rType: TRttiType;
  AMethCreate: TRttiMethod;
  instanceType: TRttiInstanceType;
begin
  ctx := TRttiContext.Create;
  rType := ctx.GetType(TypeInfo(T));
  AMethCreate := rType.GetMethod('Create');

  if Assigned(AMethCreate) and rType.IsInstance then
  begin
    instanceType := rType.AsInstance;

    AValue := AMethCreate.Invoke(instanceType.MetaclassType, []);// create parameters

    Result := AValue.AsType<T>;
  end;
end;
类函数TTest.CreateInstance:T;
变量
AValue:TValue;
ctx:trtti上下文;
rType:trttype;
方法:trtti法;
instanceType:trttInstanceType;
开始
ctx:=TRttiContext.Create;
rType:=ctx.GetType(TypeInfo(T));
创建:=rType.GetMethod('Create');
如果已分配(创建)和rType.IsInstance,则
开始
instanceType:=rType.AsInstance;
AValue:=创建.Invoke(instanceType.MetaclassType,[]);//创建参数
结果:=AValue.AsType;
结束;
结束;
更新的解决方案:

class function TTest.CreateInstance<T>: T;
var
  AValue: TValue;
  ctx: TRttiContext;
  rType: TRttiType;
  AMethCreate: TRttiMethod;
  instanceType: TRttiInstanceType;
begin
  ctx := TRttiContext.Create;
  rType := ctx.GetType(TypeInfo(T));
  for AMethCreate in rType.GetMethods do
  begin
    if (AMethCreate.IsConstructor) and (Length(AMethCreate.GetParameters) = 0) then
    begin
      instanceType := rType.AsInstance;

      AValue := AMethCreate.Invoke(instanceType.MetaclassType, []);

      Result := AValue.AsType<T>;

      Exit;
    end;
  end;
end;
类函数TTest.CreateInstance:T;
变量
AValue:TValue;
ctx:trtti上下文;
rType:trttype;
方法:trtti法;
instanceType:trttInstanceType;
开始
ctx:=TRttiContext.Create;
rType:=ctx.GetType(TypeInfo(T));
例如,在rType.GetMethods中创建
开始
如果(AMethCreate.IsConstructor)和(Length(AMethCreate.GetParameters)=0),则
开始
instanceType:=rType.AsInstance;
AValue:=create.Invoke(instanceType.MetaclassType,[]);
结果:=AValue.AsType;
出口
结束;
结束;
结束;
这样称呼它:

var
  obj: TTestObj;
begin
  obj := TTest.CreateType<TTestObj>;
var
obj:tobj;
开始
obj:=TTest.CreateType;

如果我没有弄错,泛型类型“
T
”是一个类。在这种情况下,只需声明:

Atomic< T: class > = class
原子=class
而不是公寓

Atomic< T > = class
原子=类
这将告诉编译器
T
是一种类类型,因此您可以使用构造函数和类类型的所有其他特性,而无需任何额外的解决方法


如果我对基本假设的理解是错误的,我很抱歉。

如果我将结果转换回T(我已经修复了您的示例),它将完全按照我的意愿工作-谢谢!欢迎!是的,有必要进行类型转换。结果()表明此代码调用了错误的构造函数。“更新的解决方案”Linas在这方面做得更好。谢谢,但是XE2 Update 2的核心问题是,如果T没有标记“类”约束,TypeInfo(T)将无法编译。我不知道这是一个“特性”还是一个bug?我不确定,但恐怕这是一个特性。它看起来像我关于TypeInfo(T)的陈述没有在XE2更新2上编译是错误的。对不起:(我已经用在XE2更新2上工作的解决方案更新了TOndrej的答案。不,T只是有时是一个类,其他时候不是。
Atomic< T > = class