ZeD*_*aye 8 delphi generics delphi-2010
以任何方式,是否可以将const参数数组中的通用记录传递给函数调用?
我想使用Allen Bauer 的Nullable记录在一种自制的ORM中使用"Plain Old Delphi Objects"来映射数据库行:
type
Nullable<T> = record
...
end;
TMyTableObject = class(TDbOject)
private
FId: Integer;
FOptionalField: Nullable<string>;
protected
procedure InternalSave; override;
public
property Id: Integer read FId write SetId;
property OptionalField: Nullable<string> read FOptionalField write SetOptionalField;
end;
...
implementation
procedure TMyTableObject.InternalSave;
begin
{ FDbController is declared and managed in TDbObject, it contains fonction to run queries
on the database }
FDbController.Execute(
'update or insert into MY_TABLE(TABLE_ID, OPTIONAL_FIELD) ' +
'values (?, ?) ' +
'matching(TABLE_ID) returning TABLE_ID', [FId, FOptionalField],
procedure (Fields: TSQLResult)
begin
FId := Fields.AsInteger[0];
end;
end;
end;
Run Code Online (Sandbox Code Playgroud)
上面的代码导致错误:"E2250:在没有覆盖版本'执行(:E2250 Aucune版本surchargée去"执行"东北peut理由appeléeAVEC CES参数译自法语)’功能,可以用这些参数调用"
我可以明确地转换FOptionalField为字符串或其他任何东西,因为可空覆盖特设运营商,但我真的要知道,如果可空具有价值或者直到我常量数组映射到数据库查询对象的PARAMS域:
procedure TDbContext.MapParams(Q: TUIBQuery; Params: TConstArray);
const
BOOL_STR: array[Boolean] of string = ('F', 'V');
var
i: Integer;
begin
for i := 0 to High(Params) do
case Params[i].VType of
vtInteger : Q.Params.AsInteger[i] := Params[i].VInteger;
vtInt64 : Q.Params.AsInt64[i] := Params[i].VInt64^;
vtBoolean : Q.Params.AsString[i] := BOOL_STR[Params[i].VBoolean];
vtChar : Q.Params.AsAnsiString[i] := Params[i].VChar;
vtWideChar: Q.Params.AsString[i] := Params[i].VWideChar;
vtExtended: Q.Params.AsDouble[i] := Params[i].VExtended^;
vtCurrency: Q.Params.AsCurrency[i] := Params[i].VCurrency^;
vtString : Q.Params.AsString[i] := string(Params[i].VString^);
vtPChar : Q.Params.AsAnsiString[i] := Params[i].VPChar^;
vtAnsiString: Q.Params.AsAnsiString[i] := AnsiString(Params[i].VAnsiString);
vtWideString: Q.Params.AsUnicodeString[i] := PWideChar(Params[i].VWideString);
vtVariant : Q.Params.AsVariant[i] := Params[i].VVariant^;
vtUnicodeString: Q.Params.AsUnicodeString[i] := string(Params[i].VUnicodeString);
vtPointer :
begin
if Params[i].VPointer = nil then
Q.Params.IsNull[i] := True
else
Assert(False, 'not nil pointer is not supported');
end
else
Assert(False, 'not supported');
end;
end;
Run Code Online (Sandbox Code Playgroud)
你对如何使这种结构成为可能有任何想法吗?我找到了一种方法,通过使用RTTI向Nullable中的Variant添加显式的转换操作符覆盖,但这有点棘手,因为它需要在const调用数组中显式转换为Variant:
class operator Nullable<T>.Explicit(Value: Nullable<T>): Variant;
begin
if Value.HasValue then
Result := TValue.From<T>(Value.Value).AsVariant
else
Result := Null;
end;
...
FDbController.Execute(
'update or insert into MY_TABLE(TABLE_ID, OPTIONAL_FIELD) ' +
'values (?, ?) ' +
'matching(TABLE_ID) returning TABLE_ID', [FId, Variant(FOptionalField)],
procedure (Fields: TSQLResult)
begin
FId := Fields.AsInteger[0];
end;
end;
Run Code Online (Sandbox Code Playgroud)
正如您所说,在编译器的当前状态下,参数中没有record允许array of const.
实际上,TVarRec.VType值没有任何vtRecord.
并且Variant类型本身没有varRecord由Delphi处理的类型.在Windows世界中存在这样的变体类型(例如,DotNet结构被映射到vt_RecordCOM中的类型),但这种变体不是由Delphi处理的.
可能的是将指针传递给记录的typeinfo,然后传递给记录:
case Params[i].VType of
vtPointer:
if (i<high(Params)) and (Params[i+1].VType=vtPointer) then
if Params[i].VPointer = TypeInfo(MyRecord) then begin
...
end;
Run Code Online (Sandbox Code Playgroud)
也许这是一个不太好的答案,因为你使用泛型...但至少是一个开始...在所有情况下,这就是我如何使用预先通用的Delphi编译器.TypeInfo()关键字已经非常强大,并且比"新"RTTI实现更快.
另一种可能性(与泛型兼容)应该是在记录内容中添加一些记录类型ID.然后将记录作为指针传递,读取ID,并按预期使用它:
case Params[i].VType of
vtPointer:
if Params[i].VPointer <> nil then
case PRecordType(Params[i].VPointer)^ of
aRecordType: ...
...
end;
Run Code Online (Sandbox Code Playgroud)
这可以通过简单扩展初始Nullable<T>定义来实现.
后Scriptum:
在ORM中使用记录可能不是最好的解决方案......
该class模型比record/objectDelphi中的模型更先进,并且由于ORM是关于对象建模的,因此您应该使用可用的最佳OOP模型,恕我直言.通过依赖类而不是记录,您可以拥有继承,嵌入式类型信息(通过Class方法或仅通过PPointer(aObject)^),可空值和虚拟方法(这对于ORM非常方便).即使array of const参数问题也允许直接处理这些vtObject类型,以及专用的类虚方法.基于记录的ORM只是序列化表行的一种方式,而不是使用OOP最佳实践设计应用程序.关于性能,class与复制记录的潜在问题相比,实例的内存分配不是问题(_CopyRecordRTL函数 - 由编译器以隐藏方式调用,例如对于函数参数 - 可能非常慢).
例如,在我们的ORM中,我们处理动态数组属性,甚至属性中的动态记录数组(Delphi不为已发布的记录属性生成RTTI,这是当前类实现的另一个不一致行为).我们使用"旧"RTTI 将记录和动态数组序列化为JSON或二进制文件,并且它运行良好.最好的两个对象世界.