如何将泛型 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
不是对象(例如 Integer
、String
或 OleVariant
),则它将 return nil
,并且不会崩溃。
如果我们是一个对象(例如TCustomer
、TPatron
、TSalesOrder
、TShape
),那么我们可以return这个对象就好了。
我不想混淆问题;但是如果你看看 IEnumerable
,你就会明白实际发生了什么。
红利阅读
- Delphi: determine actual type of a generic?
- Conditional behaviour based on concrete type for generic class
回答
我会让 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;
我有一个方法需要 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
不是对象(例如 Integer
、String
或 OleVariant
),则它将 return nil
,并且不会崩溃。
如果我们是一个对象(例如TCustomer
、TPatron
、TSalesOrder
、TShape
),那么我们可以return这个对象就好了。
我不想混淆问题;但是如果你看看 IEnumerable
,你就会明白实际发生了什么。
红利阅读
- Delphi: determine actual type of a generic?
- Conditional behaviour based on concrete type for generic class
回答
我会让 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;