4

有没有人有TDictionary<TKey, TValue>在其构造函数期间填充的示例代码?

4

3 回答 3

5

显然你只想要一个单行,所以我试了一下,实现了一个TDictHelper允许使用单行创建和填充字典的方法。


使用任何形式的单线初始化 Dictionary 的问题在于它需要成对的值,而我们没有必要的好语法来传递这些对。例如,如果需要TPair<Key, Value>.Create(A, B)对添加到字典中的每一对值使用语法,那将是一个丑陋的一个衬里。

我确实想出了几个好看的替代品;第一个是这样使用的:

  with TDictHelper<Integer, string> do
    Dict := Make([P(1, 'one'), P(2, 'two')]);

之所以需要使用 ,with是因为TDictHelper我实现的类有一个Make以数组TPair<Key, Value>为参数的例程;如果我把它写成这样,这将是不可用的:

Dict := TDictHelper<Integer, string>.Make(TPair<Integer, string>.Create(1, 'one'), TPair<Integer, string>.Create(2, 'two'));

它会起作用,但它会非常非常难看!

由于使用with可能会出现问题(特别是如果您想使用两种字典),我提供了另一种语法;不幸的是,这个不能扩展,它变得非常丑陋,非常快:

  Dict := TDictHelper<Integer, string>.Make([1, 2], ['one', 'two']);

此替代方法为键和值采用两个单独的数组,将它们组合在Make方法中。2-3 个元素看起来不错,但无法扩展:如果您有 10 个元素并且需要删除第 7 个元素怎么办?您需要对元素进行计数,这很容易出错。

这是完整的代码,不多:

program Project25;

{$APPTYPE CONSOLE}

uses
  SysUtils, Generics.Collections;

type
  TDictHelper<Key, Value> = class
  public
    class function P(const K:Key; const V:Value): TPair<Key, Value>;
    class function Make(init: array of TPair<Key, Value>): TDictionary<Key, Value>;overload;
    class function Make(KeyArray: array of Key; ValueArray: array of Value): TDictionary<Key, Value>;overload;
  end;

{ TDictHelper<Key, Value> }

class function TDictHelper<Key, Value>.Make(init: array of TPair<Key, Value>): TDictionary<Key, Value>;
var P: TPair<Key, Value>;
begin
  Result := TDictionary<Key, Value>.Create;
  for P in init do
    Result.AddOrSetValue(P.Key, P.Value);
end;

class function TDictHelper<Key, Value>.Make(KeyArray: array of Key;
  ValueArray: array of Value): TDictionary<Key, Value>;
var i:Integer;
begin
  if Length(KeyArray) <> Length(ValueArray) then
    raise Exception.Create('Number of keys does not match number of values.');
  Result := TDictionary<Key, Value>.Create;
  for i:=0 to High(KeyArray) do
    Result.AddOrSetValue(KeyArray[i], ValueArray[i]);
end;

class function TDictHelper<Key, Value>.P(const K: Key;
  const V: Value): TPair<Key, Value>;
begin
  Result := TPair<Key, Value>.Create(K, V);
end;

// ============================== TEST CODE FOLLOWS

var Dict: TDictionary<Integer, string>;
    Pair: TPair<Integer, string>;

begin
  try
    try
      // Nice-looking but requires "with" and you can't work with two kinds of DictHelper at once
      with TDictHelper<Integer, string> do
        Dict := Make([P(1, 'one'), P(2, 'two')]);
      // Use the array
      for Pair in Dict do
        WriteLn(Pair.Key, ' = ', Pair.Value);
      Dict.Free;

      // Passing the Keys and the Values in separate arrays; Works without "with" but it would
      // be difficult to maintain for larger number of key/value pairs
      Dict := TDictHelper<Integer, string>.Make([1, 2], ['one', 'two']);
      // Use the array
      for Pair in Dict do
        WriteLn(Pair.Key, ' = ', Pair.Value);
      Dict.Free;

    except on E:Exception do
      WriteLn(E.ClassName, #13#10, E.Message);
    end;
  finally ReadLn;
  end;
end.
于 2013-01-24T07:16:48.207 回答
4

您需要调用接收类型参数的字典构造函数重载CollectionTEnumerable<TPair<TKey, TValue>>

例如,假设我们有TDictionary<string, Integer>. 然后我们可以向构造函数传递一个TEnumerable<TPair<string, Integer>>. 这种事情的一个例子是TList<TPair<string, Integer>>

List := TList<TPair<string, Integer>>.Create;
List.Add(TPair<string, Integer>.Create('Foo', 42));
List.Add(TPair<string, Integer>.Create('Bar', 666));
Dictionary := TDictionary<string, Integer>.Create(List);

这是非常笨拙的,你永远不会喜欢这个选项,而不是简单Create的,然后是一系列对Add. 如果您碰巧手头有现成的收藏,您只会使用传递现有收藏的选项。

派生自的类的另一个示例TEnumerable<T>TDictionary它本身:

type
  TDictionary<TKey,TValue> = class(TEnumerable<TPair<TKey,TValue>>)

因此,如果您已经有一个字典实例,您可以创建另一个并使用第一个的内容对其进行初始化:

Dict2 := TDictionary<string, Integer>.Create(Dict1);
于 2013-01-23T22:29:34.283 回答
1

在下面的示例中,键和值的数组被传递给自定义构造函数。键和值使用以下模式放置在同一个数组中:key1, value1, key2, value2, ...., keyN, valueN。该数组必须包含偶数个项目。

unit MainUnit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Generics.Collections, System.Rtti;


type
  TForm3 = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TMyDictionary<TK, TV> = class(TDictionary<TK,TV>)
    constructor Create(const values: array of variant);
  end;

var
  Form3: TForm3;
  extensions: TMyDictionary<string, integer>;

implementation

constructor TMyDictionary<TK, TV>.Create(const values: array of variant);
var
  I: Integer;
  k, v: TValue;
  kt: TK;
  vt: TV;
begin
  inherited Create(Length(values) div 2);
  I := Low(values);
  while i <= High(values)  do
  begin
    k := TValue.FromVariant(values[i]);
    v := TValue.FromVariant(values[i + 1]);
    kt := k.AsType<TK>;
    vt := v.AsType<TV>;
    Add(kt, vt);
    Inc(I, 2);
  end;

end;

{$R *.dfm}
begin

 extensions := TMyDictionary<string, integer>.Create(['1', 1, '3', 3]);

 OutputDebugString(PChar(IntToStr(extensions['1'])));
end.

我不太确定 TValue 方法的性能,但是如果您有一些项目,我认为可以忽略不计。

于 2015-07-15T17:46:02.737 回答