针对TStrings和TStringList的接口的代码

时间:2012-02-01 12:37:47

标签: delphi interface delphi-xe2 tstringlist

我感兴趣地阅读了Nick Hodges博客Why You Should Be Using Interfaces 因为我已经爱上了编码中更高层次的接口,所以我决定看看如何将它扩展到相当低的水平,并研究在VCL类中存在哪些支持。

我需要的一个常见构造是使用TStringList做一些简单的事情,例如这段代码将一个小文本文件列表加载到逗号文本字符串中:

var
  MyList : TStrings;
  sCommaText : string;
begin
  MyList := TStringList.Create;
  try
    MyList.LoadFromFile( 'c:\temp\somefile.txt' );
    sCommaText := MyList.CommaText;

    // ... do something with sCommaText.....

  finally
    MyList.Free;
  end;
end;

如果我可以使用MyList作为接口编写它似乎是一个很好的简化 - 它将摆脱try-finally并提高可读性:

var
  MyList : IStrings;
         //^^^^^^^
  sCommaText : string;
begin
  MyList := TStringList.Create;
  MyList.LoadFromFile( 'c:\temp\somefile.txt' );
  sCommaText := MyList.CommaText;

  // ... do something with sCommaText.....

end;

我看不到定义的IStrings - 当然不是在Classes.pas中,尽管在线编程时有关于它的引用。它存在吗?这是一个有效的简化吗?我正在使用Delphi XE2。

2 个答案:

答案 0 :(得分:6)

RTL / VCL中没有可以满足您需要的界面(公开与TStrings相同的界面)。如果你想使用这样的东西,你需要自己发明它。

您可以使用这样的包装器实现它:

type
  IStrings = interface
    function Add(const S: string): Integer;
  end;

  TIStrings = class(TInterfacedObject, IStrings)
  private
    FStrings: TStrings;
  public
    constructor Create(Strings: TStrings);
    destructor Destroy; override;
    function Add(const S: string): Integer;
  end;

constructor TIStrings.Create(Strings: TStrings);
begin
  inherited Create;
  FStrings := Strings;
end;

destructor TIStrings.Destroy;
begin
  FStrings.Free; // don't use FreeAndNil because Nick might see this code ;-)
  inherited;
end;

function TIStrings.Add(const S: string): Integer;
begin
  Result := FStrings.Add(S);
end;

当然,你会将TStrings接口的其余部分包装在真正的类中。使用这样的包装器类来执行它,这样您就可以通过访问它的实例来包装任何类型的TStrings

像这样使用:

var
  MyList : IStrings;
....
MyList := TIStrings.Create(TStringList.Create);

您可能更愿意添加辅助函数来实际调用TIStrings.Create的脏工作。

另请注意,生命周期可能是一个问题。您可能需要此包装器的变体,它不会接管对基础TStrings实例的生命周期的管理。可以使用TIStrings构造函数参数进行排列。


我自己,我认为这是一个有趣的思想实验,但并不是一个明智的方法。 TStrings类是一个抽象类,它具有接口提供的所有好处。我认为按原样使用它并没有真正的缺点。

答案 1 :(得分:4)

由于TStrings是一个抽象类,因此它的接口版本不会提供太多。无论如何,该接口的任何实现者肯定都是TStrings后代,因为没有人愿意重新实现TStrings所做的所有事情。我认为有两个原因需要TStrings接口:

  1. 自动资源清理。您不需要TStrings特定的界面。而是使用JCL中的ISafeGuard接口。这是一个例子:

    var
      G: ISafeGuard;
      MyList: TStrings;
      sCommaText: string;
    begin
      MyList := TStrings(Guard(TStringList.Create, G));
    
      MyList.LoadFromFile('c:\temp\somefile.txt');
      sCommaText := MyList.CommaText;
    
      // ... do something with sCommaText.....
    end;
    

    要保护应具有相同生命周期的多个对象,请使用IMultiSafeGuard

  2. 与外部模块互操作。这是IStrings的用途。 Delphi使用TStringsAdapter类实现它,当您在现有GetOleStrings后代上调用TStrings时会返回该类。当您有字符串列表并且需要授予对需要IStringsIEnumString接口的另一个模块的访问权限时,请使用它。这些接口很笨拙,否则它们都没有提供TStrings所做的所有事情 - 所以除非必须这样做,否则不要使用它们。

    如果你正在使用的外部模块是保证将始终使用与编译模块相同的Delphi版本编译,那么你应该使用运行时包和直接传递TStrings个后代。共享包允许两个模块使用相同的类定义,并且内存管理大大简化。

相关问题