【问题标题】:Wrapping TStringList in a Record在记录中包装 TStringList
【发布时间】:2009-08-01 15:27:34
【问题描述】:

我倾向于使用 Delphi 的 TStringList 进行文本操作,所以我编写了很多程序/函数,例如:

var
  TempList: TStringList;
begin
  TempList:= TStringList.Create;
  try
    // blah blah blah do stuff with TempList   


  finally
    TempList.Free;
  end;
end;

为这样一个常见的实用程序类取消创建和释放会很好。

既然我们现在有了带有方法的记录,是否可以将像 TStringList 这样的类包装在一个 记录,这样我就可以拥有:

var
  TempList: TRecordStringList;
begin
  // blah blah blah do stuff with TempList   


end;

【问题讨论】:

    标签: delphi records tstringlist


    【解决方案1】:

    这是可能的。创建一个公开你想要的方法/对象的接口:

    type
      IStringList = interface
        procedure Add(const s: string); // etc.
        property StringList: TStringList read GetStringList; // etc.
      end;
    

    实现接口,并让它包装一个真正的TStringList

    type
      TStringListImpl = class(TInterfacedObject, IStringList)
      private
        FStringList: TStringList; // create in constructor, destroy in destructor
        // implementation etc.
      end;
    

    然后实现记录:

    type
      TStringListRecord = record
      private
        FImpl: IStringList;
        function GetImpl: IStringList; // creates TStringListImpl if FImpl is nil
                                       // returns value of FImpl otherwise
      public
        procedure Add(const s: string); // forward to GetImpl.Add
        property StringList: TStringList read GetStringList; // forward to
                                                             // GetImpl.StringList
        // etc.
      end;
    

    记录中存在接口这一事实意味着编译器将自动处理引用计数,在创建和销毁副本时调用 _AddRef 和 _Release,因此生命周期管理是自动的。这适用于永远不会包含对自身的引用(创建循环)的对象 - 引用计数需要各种技巧来克服引用图中的循环。

    【讨论】:

    • Barry:将这个想法用于程序中的所有数据结构是否是一种有效处理垃圾收集的方法?
    • 否;请参阅上面的最后一句话,关于参考图中的循环。解决这个问题可能非常棘手。
    • 这很酷。澄清一下,只有当对象实例包含对其自身实例或同一类的任何实例的引用时才会出现问题?
    • 这里我们称记录为R,实例为I。如果有引用链,I -> R -> I -> [...] -> I -> R -> I,其中任何两个 I 实例都是同一个对象,那么就会有问题。但这对于仅实例来说是一个问题,除了记录包装器之外,因为对象需要明确的所有权语义 - 并非所有引用对象的东西都可以调用 Free,只有所有者。引用计数——记录包装器策略实现的——在所有引用之间分配所有权,唯一的缺点是循环让对象“拥有”自己。那些不能使用包装器。
    • 已经在 StdVCL 中声明了 IStrings 接口,在 AxCtrls 中声明了 TStringsAdapter,如果您愿意,可以将其重用于此目的,而不是如示例中所示将它们重新实现为 IStringList 和 TStringListImpl。
    【解决方案2】:

    如果您有幸升级到 Delphi 2009,请查看Barry's work with smart pointers

    TSmartPointer<T: class> = record
    strict private
      FValue: T;
      FLifetime: IInterface;
    public
      constructor Create(const AValue: T); overload;
      class operator Implicit(const AValue: T): TSmartPointer<T>;
      property Value: T read FValue;
    end;
    

    它们真的很酷,但需要泛型和匿名方法。如果你还没有升级到Delphi 2009,那就赶紧行动吧!尤其是在他们提供BOGO special 的时候。您还可以通过downloading the trial 获得 Marco 的Delphi 开发人员手册免费。我也已经购买了它的副本。

    【讨论】:

    • 已经升级了!从 Turbo Delphi Pro 升级到 Delphi 2009 Professional ,所以(就我而言)我得到了它。 BOGO 的报价本来是锦上添花,但无论如何我也在考虑购买 Prism。
    • 虽然我还没有接触过泛型——要学的东西太多,时间太少......
    【解决方案3】:

    已经有另一个例子implemented in CC

    StringList 与 TStringList 相同,只是它是一个值 类型。它不必被创建、销毁或放入 尝试/最后。这是由编译器为您完成的。有 这些工作几乎没有特殊的性能损失:

    var 
      strings: StringList;
      astr: string;
    begin
      strings.Add('test1');
      strings.Add('test2');
      aStr := string(strings);
      RichEdit.Lines.AddStrings(strings);
    end;
    

    代码可以用作模板,将任何 TObject 包装为值类类型。

    它已经为您公开了 TStringList 的所有内容。

    【讨论】:

    • 谢谢吉姆,我在发这个之前搜索了抄送和谷歌,但没有找到这个。有一堆实用程序类对象会很方便。虽然我有点喜欢 RStringList ;-)
    • 这不适用于 x64 架构,因为似乎 InterlockedIncrement/InterlockedDecrement 不是由 kernel32 导出的(在 64 位版本中)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-22
    • 1970-01-01
    • 1970-01-01
    • 2010-09-14
    • 2011-12-09
    • 2011-08-04
    • 1970-01-01
    相关资源
    最近更新 更多