简体   繁体   English

基于不受约束的泛型类型创建对象实例

[英]Creating object instance based on unconstrained generic type

I have unconstrained generic type Atomic which implements an initializer (details in my previous question ). 我有一个不受约束的泛型Atomic类型,它实现了一个初始化程序(在上一个问题中有详细介绍)。

type
  Atomic<T> = class
    type TFactory = reference to function: T;
    class function Initialize(var storage: T; factory: TFactory): T;
  end;

Now I want to write simplified Initialize function which would take the type information from T (provided that typeof(T) is tkClass) and create new instance (when necessary) with the default constructor. 现在,我想编写简化的Initialize函数,该函数将从T中获取类型信息(假设typeof(T)为tkClass),并使用默认构造函数创建新实例(必要时)。

Sadly, this fails: 可悲的是,这失败了:

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;

Compiler reports error E2571 Type parameter 'T' doesn't have class or interface constraint . 编译器报告错误E2571 Type parameter 'T' doesn't have class or interface constraint

How can I trick the compiler to create an instance of class T? 如何欺骗编译器创建类T的实例?

You can use GetTypeData to obtain the class reference: 您可以使用GetTypeData获取类引用:

Result := T(GetTypeData(PTypeInfo(TypeInfo(T)))^.ClassType.Create);

In Delphi XE2 (and hopefully in next releases), you can do: 在Delphi XE2中(并希望在以后的版本中),您可以执行以下操作:

var
  xInValue, xOutValue: TValue;

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

(This rather circumvent way was discovered by used cjsalamon in the OmniThreadLibrary forum: Error in OtlSync XE2 .) (此相当的回避方式被使用发现cjsalamon在OmniThreadLibrary论坛: 在OtlSync XE2错误 。)

You can use the new Delphi Rtti to make this task. 您可以使用新的Delphi Rtti来完成此任务。 The drawback of given solution is that it won't work if the constructor isn't named as Create. 给定解决方案的缺点是,如果构造函数未命名为Create,则它将无法正常工作。 If you need to make it work all the time, just enumerate your type methods, check if it's a constructor and have 0 parameters and then invoke it. 如果需要使其始终运行,只需枚举类型方法,检查它是否是构造函数并具有0个参数,然后调用它即可。 Works in Delphi XE. 在Delphi XE中工作。 Sample code: 样例代码:

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;

Updated solution: 更新的解决方案:

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;

And call it like this: 并这样称呼它:

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

If I got it right, the generic type " T " is a class. 如果我做对了,则通用类型“ T ”是一个类。 In this case, just declare: 在这种情况下,只需声明:

Atomic< T: class > = class

instead of the flat 而不是单位

Atomic< T > = class

This will tell to the compiler that T is a class type, so you'll be able to use the constructor and all the other features of class type without any extra workaround. 这将告诉编译器T是一个类类型,因此您将能够使用构造函数以及类类型的所有其他功能,而无需任何其他解决方法。

If my understanding was wrong in the base assumption, I apologize. 如果在基本假设中我的理解是错误的,我深表歉意。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM