如何将泛型 T 转换为 TObject?

How to cast generic T to a TObject?

我有一个方法需要 return 一个对象。当然,只有 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;

如果 T 不是对象(例如 IntegerStringOleVariant),则它将 return nil,并且不会崩溃。

如果我们是一个对象(例如TCustomerTPatronTSalesOrderTShape),那么我们可以return这个对象就好了。

我不想混淆问题;但是如果你看看 IEnumerable,你就会明白实际发生了什么。

红利阅读

回答

我会让 TLama copy/paste 得到他的答案:

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;

是的,它可以通过 TValue 完成,但这不是正确的方法。我相信让编译器验证它是一个 TObject(或后代)要好得多。

unit GrobberU;

interface

type
  TGrobber<T : Class> = class
  public
    function Swipe: TObject;
  end;

implementation

{ TGrobber<T> }

function TGrobber<T>.Swipe: TObject;
begin
  Result := T;
end;

end.

那你就可以测试一下了:

procedure TForm37.FormCreate(Sender: TObject);
var
  Grobber1 : TGrobber<TEdit>;
  Grobber2 : TGrobber<Integer>; <-- Does not compile    
begin
end;

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

type
  TGrobber<T: class> = class
    ....
  end;

或者如果类型必须从特定的 class 派生,那么可以像这样指定约束:

type
  TGrobber<T: TMyObject> = class
    ....
  end;

应用约束后,您只需直接赋值即可。

Result := current;

这之所以成为可能,是因为编译器对您的泛型类型强制执行约束。因此知道赋值对所有可能的实例都是有效的。

我会评论说,通用 class 具有 returning TObject 的功能似乎很奇怪。为什么你的函数 return T 没有?

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

Result := PObject(@current)^;

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

对于它的价值,因为 Delphi XE7 使用 System.GetTypeKind:

检查类型的种类更简单
if GetTypeKind(T) = tkClass then
  Result := PObject(@current)^
else
  Result := nil;