有没有人有 TDictionary<TKey, TValue>
在构造函数期间被填充的示例代码?
显然,您只需要一行代码,因此我尝试实现了一个TDictHelper
,它允许使用一行代码创建和填充字典。
使用任何形式的一行代码初始化字典的问题在于它需要值对,并且我们没有所需的良好语法来传递这些值对。例如,如果需要为添加到字典中的每个值对使用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']);
这种替代方法需要为Key和Value分别设置两个不同的数组,并在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.
TEnumerable<TPair<TKey, TValue>>
的Collection参数的字典构造函数重载。
例如,假设我们有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);
TEnumerable<TPair<TKey, TValue>>
的类实例。您可以编写一个辅助函数来创建其中之一,以便进行内联编码。这与 Python 版本相距甚远:Dictionary = {'Foo': 42, 'Bar': 666}
。 - David HeffernanIEnumerable<>
。 - Cosmin Prundwith
运算符,这样我就可以使用帮助器P
类函数。在Delphi中没有语法来初始化一个漂亮的单行记录,你需要调用一个构造函数。 - Cosmin Prundunit 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.
TDictionary<>
。当然,我的理解也可能是错误的。 - Cosmin Prund