如何将通用T转换为TObject?

Ian*_*oyd 8 delphi generics

我有一个方法需要返回一个对象.当然,只有T 作为一个对象才有意义:

function TGrobber<T>.Swipe: TObject;
var
   current: T;
begin
    {
       If generic T is not an object, then there's nothing we can return
       But we'll do the caller a favor and not crash horribly.
    }
    if PTypeInfo(TypeInfo(T))^.Kind <> tkClass then
    begin
       Result := nil;
       Exit;
    end;

    //We *are* an object, return the object that we are.
    current := Self.SwipeT;

    Result := TObject(current); <--E2089 invalid class typecast
end;
Run Code Online (Sandbox Code Playgroud)

如果T不是一个对象(例如Integer,StringOleVariant),然后将返回nil,而不是可怕的崩溃.

如果我们是一个对象(例如TCustomer,TPatron,TSalesOrder,TShape),那么我们就可以返回该对象就好了.

我不想混淆这个问题; 但如果你看一下IEnumerable,你会看到实际发生的事情.

奖金阅读

回答

我会让TLama复制/粘贴答案以获得他的信誉:

function TGrobber<T>.Swipe: TObject;
var
   current: T;
   v: TValue;
begin
    current := Self.SwipeT;
    v := TValue.From<T>(current);
    {
       If generic T is not an object, then there's nothing we can return
       But we'll do the caller a favor and not crash horribly.
    }
    if not v.IsObject then
    begin
       Result := nil;
       Exit;
    end;

    Result := v.AsObject;
end;
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 5

我看到两个主要选择。如果泛型类型必须是类类型,并且这是在编译时知道的,则应该对该类型应用约束:

type
  TGrobber<T: class> = class
    ....
  end;
Run Code Online (Sandbox Code Playgroud)

或者,如果类型必须派生自特定类,则可以像这样指定该约束:

type
  TGrobber<T: TMyObject> = class
    ....
  end;
Run Code Online (Sandbox Code Playgroud)

应用约束后,您只需要直接分配即可。

Result := current;
Run Code Online (Sandbox Code Playgroud)

这是可能的,因为编译器强制对泛型类型进行约束。因此知道该分配对于所有可能的实例化都是有效的。

我想说的是,泛型类有一个返回的函数似乎很奇怪TObject。为什么你的函数不返回T

如果您无法约束,那么简单的指针类型转换是最干净的方法:

Result := PObject(@current)^;
Run Code Online (Sandbox Code Playgroud)

显然,您需要检查它T是一个类类型,即您已经掌握的代码。

就其价值而言,从 Delphi XE7 开始,使用以下命令检查类型的种类更加简单System.GetTypeKind

if GetTypeKind(T) = tkClass then
  Result := PObject(@current)^
else
  Result := nil;
Run Code Online (Sandbox Code Playgroud)