通过 DataSnap 崩溃的 TDictionary,FComparer 为零
TDictionary via DataSnap crashes, FComparer is nil
我在 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 总是得到一个值。
我(或Delphi)做错了什么?
"What is Delphi doing wrong"的答案是:
DataSnap 使用单元 Data.DBXJSONReflect
中的 TJsonMarshal
和 TJsonUnmarshal
。解组后,通过调用无参数构造函数创建 TDictionary<X,Y>
的实例。这里的无参数构造函数是直接继承自 TObject
的构造函数。
然而,当 你 时,键入 TDictionary<X, Y>.Create();
你将使用默认参数 (Create(ACapacity: Integer = 0);
) 调用 "correct" 构造函数。然而,TJsonUnmarshall
class 没有,因为它正在寻找一个 确实没有参数 的构造函数。你通常调用的那个有一个参数,即使你不必传递它。
我不知道 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.
我在 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 总是得到一个值。
我(或Delphi)做错了什么?
"What is Delphi doing wrong"的答案是:
DataSnap 使用单元 Data.DBXJSONReflect
中的 TJsonMarshal
和 TJsonUnmarshal
。解组后,通过调用无参数构造函数创建 TDictionary<X,Y>
的实例。这里的无参数构造函数是直接继承自 TObject
的构造函数。
然而,当 你 时,键入 TDictionary<X, Y>.Create();
你将使用默认参数 (Create(ACapacity: Integer = 0);
) 调用 "correct" 构造函数。然而,TJsonUnmarshall
class 没有,因为它正在寻找一个 确实没有参数 的构造函数。你通常调用的那个有一个参数,即使你不必传递它。
我不知道 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.