通过 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 中的 TJsonMarshalTJsonUnmarshal。解组后,通过调用无参数构造函数创建 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.