我在DataSnap客户端中遇到字典崩溃,因为它的FComparer在某种程度上是零。
服务器端代码:
TColorNames = TDictionary<integer, string>;
function TServerMethods.DictionaryTest: TColorNames;
begin
result := TColorNames.Create;
result.Add (1, 'Red');
result.Add (2, 'Blue');
end;
客户端代码:
procedure TformClientMain.FetchColors;
var
Colors: TColorNames;
begin
Colors := easServer.DictionaryTest;
if Colors.Items[1]<>'Red'
then ShowMessage('Not red');
end;
Colors.Items[1]
崩溃(以及需要FComparer的其他功能)。当函数尝试访问FComparer时,崩溃发生在System.Generics.Collections中。
function TDictionary<TKey,TValue>.Hash(const Key: TKey): Integer;
我确实收到了列表中的所有数据,只需使用for color in Colors.Values do ShowMessage(Color);
循环播放它就可以了。
当我使用TColorNames.Create创建字典实例时,在客户端或服务器端,FComparer有一个值,并且这些问题不存在。我在字典构造函数中放置断点并在datasnap调用期间跟踪代码 - FComparer总是获取一个值。
我(或德尔福)做错了什么?
答案 0 :(得分:5)
“德尔福做错了什么”的答案是:
DataSnap使用单位TJsonMarshal
中的TJsonUnmarshal
和Data.DBXJSONReflect
。在解组时,通过调用无参数构造函数创建TDictionary<X,Y>
的实例。这里的无参数构造函数是直接从TObject
继承的构造函数。
当你时,键入TDictionary<X, Y>.Create();
,你正在使用默认参数(Create(ACapacity: Integer = 0);
)调用“正确”的构造函数。但是,TJsonUnmarshall
类没有,因为它正在寻找实际上没有参数的构造函数。您通常调用的那个有一个参数,即使您不必通过它。
我不知道DataSnap是如何工作的,但你应该能够将自定义编组和解组器传递给序列化。
由于Embarcadero已将我所知道的所有错误报告(example)关闭为“按预期工作”,因此可以安全地认为通用收藏品不应来回编组。你可能应该恢复数组。
以下是重现的最小代码:
unit Unit1;
interface
uses
System.Generics.Collections,
System.JSON,
Data.DBXJSONReflect;
type
TColorNames = TDictionary<Integer, String>;
procedure p();
implementation
procedure p();
var
original: TColorNames;
marshaller: TJSONMarshal;
unmarshaller: TJSONUnMarshal;
asJson: TJsonValue;
marshalledBack: TColorNames;
begin
original := TColorNames.Create();
marshaller := TJsonMarshal.Create();
asJson := marshaller.Marshal(original);
unmarshaller := TJSONUnMarshal.Create();
marshalledBack := unmarshaller.Unmarshal(asJson) as TColorNames;
marshalledBack.Add(0, ''); // << will crash because FComparer is nil
end;
end.