【问题标题】:Generics and Marshal / UnMarshal. What am I missing here?泛型和元帅/UnMarshal。我在这里想念什么?
【发布时间】:2011-09-23 12:19:31
【问题描述】:

最好提一下: 我正在使用 Delphi XE2 - 但 XE 或 2010 也应该可以解决问题 :-)

这个问题现在在 Quality Central QC#99313 请投票 :-)

截至 2011 年 10 月 20 日,Embarcadero 已将 QC 报告标记为“已解决”。 SilverKnight 提供了解决方案。但是 Embarcadero 缺乏信息让我担心。由于该解决方案建议使用 XE(2) 帮助系统、其他论坛和 CC 中解释的源代码以外的其他源代码。但是你自己看看 QC。

鉴于这些类型声明:

type
TTestObject : Class
    aList : TStringList;
    function Marshal : TJSonObject;
  end;

  TTestObjectList<T:TestObject> : Class(TObjectList<T>)
    function Marshal : TJSonObject; // How to write this ? 
  end;

我想为 TTestObjectList 实现一个 Marshal 方法。 据我所知 - 我应该为 TTestObject 和美丽注册一个转换器 其中 - 为每个元素调用 Marshal。

TTestObject 的 Marshal 注册了这个转换器:

RegisterConverter(TStringList,
  function(Data: TObject): TListOfStrings
  var
    i, Count: Integer;
  begin
    Count := TStringList(Data).Count;
    SetLength(Result, Count);
    for i := 0 to Count - 1 do
      Result[i] := TStringList(Data)[i];
  end);

通用 TTestObjectList Marshal 方法:

function TTestObjectList<T>.Marshal: TJSONObject;
var
Mar : TJsonMarshal;  // is actually a property on the list.
begin
  Mar := TJsonMarshal.Create(TJSONConverter.Create);
  try
    RegisterConverter(TTestObject,
      function(Data: TObject): TObject
      begin
        Result := TTestObject(Data).Marshal;
      end);
    Result := Mar.Marshal(Self) as TJSONObject;
  finally
    Mar.Free;
  end;
end;

这是使用列表的简化示例。

var
  aTestobj : TTestObject; 
  aList : TTestObjectList<TTestObject>;
  aJsonObject : TJsonObject;
begin
  aTestObj := TTestObject.Create; // constructor creates and fills TStringlist with dummy data.
  aJsonObject := aTestObj.Marshal; // This works as intended.

  aList := TTestObjectList<TTestObject>.Create;
  aJsonObject := aList.Marshal; // Fails with tkpointer is unknown .... 
end;

当然,我有类似的恢复(解组)功能。 但是上面的代码应该可以工作 - 至少据我所知。

所以如果有人可以指出我:

为什么列表无法编组?

我知道我的列表中有 TJsonMarshal 属性 - 但它也有一个转换器/恢复器。

更改为 TTypeStringConverter(而不是 TTypeObjectConverter)将返回一个有效字符串。但我一直喜欢在 TJsonObject 上工作的想法。否则,在从字符串解组到 TTestObject 时,我会遇到同样的问题(或类似的问题)。

欢迎任何建议/想法。

【问题讨论】:

  • 我不敢相信 Embarcadero 将其标记为已修复 --- 这是一种解决方法 - 如果已修复,他们将不会有一个允许您传入自己的实例的重载构造函数!或者,在这种特殊情况下,对象应该在使用 EITHER 构造函数时起作用。我会说仍然存在一个错误。我已经在 QA 中发布了让他们重新考虑将其关闭为“用户错误”

标签: delphi generics marshalling


【解决方案1】:

这是“解决”Delphi XE2 上的问题的“解决方法”(我能够复制相同的运行时错误)。

请注意,在创建 Marshal 变量时,项目中定义了以下代码:

Marshal := TJSONMarshal.Create(TJSONConverter.Create);

将两行更改为:

Marshal := TJSONMarshal.Create;  //use the default constructor - which does the same thing as TJSONConvert.Create already

解决了问题 - TOndrej 提供的测试应用程序然后执行而没有错误。

【讨论】:

  • +1 为那个 :-) ...我必须承认,将 TJSONMarshal.Create 与构造函数一起使用是一种习惯。但我不知道如果没有 .. thx 的信息,它实际上做了同样的事情。
【解决方案2】:

我不确定您为什么会收到该错误。在 Delphi XE 中,以下似乎对我有用:

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes, Contnrs,
  Generics.Defaults, Generics.Collections,
  DbxJson, DbxJsonReflect;

type
  TTestObject = class(TObject)
    aList : TStringList;
    function Marshal : TJSonObject;
  public
    constructor Create;
    destructor Destroy; override;
  end;

  TTestObjectList<T:TTestObject,constructor> = class(TObjectList<T>)
    function Marshal: TJSonObject;
    constructor Create;
  end;

{ TTestObject }

constructor TTestObject.Create;
begin
  inherited Create;
  aList := TStringList.Create;
  aList.Add('one');
  aList.Add('two');
  aList.Add('three');
end;

destructor TTestObject.Destroy;
begin
  aList.Free;
  inherited;
end;

function TTestObject.Marshal: TJSonObject;
var
  Marshal: TJSONMarshal;
begin
  Marshal := TJSONMarshal.Create(TJSONConverter.Create);
  try
    Marshal.RegisterConverter(TStringList,
      function (Data: TObject): TListOfStrings
      var
        I, Count: Integer;
      begin
        Count := TStringList(Data).Count;
        SetLength(Result, Count);
        for I := 0 to Count - 1 do
          Result[I] := TStringList(Data)[I];
      end
      );
    Result := Marshal.Marshal(Self) as TJSONObject;
  finally
    Marshal.Free;
  end;
end;

{ TTestObjectList<T> }

constructor TTestObjectList<T>.Create;
begin
  inherited Create;
  Add(T.Create);
  Add(T.Create);
end;

function TTestObjectList<T>.Marshal: TJSonObject;
var
  Marshal: TJsonMarshal;
begin
  Marshal := TJSONMarshal.Create(TJSONConverter.Create);
  try
    Marshal.RegisterConverter(TTestObject,
      function (Data: TObject): TObject
      begin
        Result := T(Data).Marshal;
      end
      );
    Result := Marshal.Marshal(Self) as TJSONObject;
  finally
    Marshal.Free;
  end;
end;

procedure Main;
var
  aTestobj : TTestObject;
  aList : TTestObjectList<TTestObject>;
  aJsonObject : TJsonObject;
begin
  aTestObj := TTestObject.Create;
  aJsonObject := aTestObj.Marshal;
  Writeln(aJsonObject.ToString);

  Writeln;
  aList := TTestObjectList<TTestObject>.Create;
  aJsonObject := aList.Marshal;
  Writeln(aJsonObject.ToString);

  Readln;
end;

begin
  try
    Main;
  except
    on E: Exception do
    begin
      ExitCode := 1;
      Writeln(Format('[%s] %s', [E.ClassName, E.Message]));
    end;
  end;
end.

【讨论】:

  • 刚刚在 XE 中尝试过您的代码 - 我必须承认。有用。我将不得不尝试一些更复杂的事情。给我几个小时,我会回来的。我想调查一个更大的项目 - 看看是否出现问题。
  • 再次嗨.. 抱歉耽搁了。我现在已经在 XE2 中测试了代码......它完全像我的代码一样失败了????也许 XE2 中的编组存在问题?我是泛型的新手——在类型声明中包含构造函数的方式绝对很棒。为此,我愿意接受你的代码作为答案——即使它只适用于 XE :-)
  • 问题现在升级为 QC#99313。使用 TOndrej 的代码示例。
猜你喜欢
  • 2011-12-02
  • 1970-01-01
  • 2010-11-22
  • 2017-06-10
  • 2023-03-31
  • 2015-06-21
  • 1970-01-01
  • 1970-01-01
  • 2020-08-04
相关资源
最近更新 更多