如何重构数组之间的 copying/moving 数据?
How to refactor copying/moving data between arrays?
我有一个进程将数据读入 150 多个临时数组,处理数据并将临时数组中的数据复制到工作数组中。工作数组在一个全局数组中,因此我可以导入多个数据,这意味着我可以重复相同的过程多达 100 次,并最终得到一组包含 100 倍工作数据的大数组,我可以使用、比较和做一些事情。
我有 150 多个数组,所以 150 次:
// for each array
SetLength(myData[Idx].WorkNames,Length(tmpNames)); // <- prepare to copy
for i := 0 to High(tmpNames) do // <- copy
myData[Idx].WorkNames[i]:=tmpNames[i];
SetLength(tmpNames,0); // <- clear tmp array
每个数组 4 行代码 - 150x4 = 600 loc + 初始 + 空行 - 大约 900 loc.
以下是我所做的示例:
type
TName = record
NameID:integer;
Description:string;
end;
TItem = record
ItemID:integer;
Description:string;
Active:boolean;
end;
TWorkData = record
WorkDataType:string;
WorkNames:array of TName;
WorkItems:array of TItem;
end;
var
AllWorkData:array of TWorkData; // <- global array that has all work data - up to 100x sets of work data
tmpNames:array of TName; // <- tmp arrays before saving to work array
tmpItems:array of TItem; //
procedure TForm1.Button1Click(Sender: TObject);
var i,Idx:integer;
begin
// 1. read data into tmp arrays
ReadDataIntoTmpArrays;
ProcessTmpData;
// 2. copy tmp arrays into work data
Idx:=GetWorkDataIdx; // <- work data sequence number; start with 0
AllWorkData[Idx].WorkDataType:=GetWorkDataName(Idx);
SetLength(AllWorkData[Idx].WorkNames,Length(tmpNames));
SetLength(AllWorkData[Idx].WorkItems,Length(tmpItems));
for i := 0 to High(tmpNames) do
AllWorkData[Idx].WorkNames[i]:=tmpNames[i];
for i := 0 to High(tmpItems) do
AllWorkData[Idx].WorkItems[i]:=tmpItems[i];
// 3. clear tmp arrays
SetLength(tmpNames,0);
SetLength(tmpItems,0);
end;
问题:有什么我可以做的更容易维护,重构代码吗?
如果你真的想复制,那么使用泛型来实现。您可以从 System.Generics.Collections
中声明的静态 class 方法的 TArray
class 派生。例如:
type
TArray = class(Generics.Collections.TArray)
public
class function Copy<T>(const Source: array of T; Index, Count: Integer): TArray<T>; overload; static;
class function Copy<T>(const Source: array of T): TArray<T>; overload; static;
end;
....
class function TArray.Copy<T>(const Source: array of T; Index, Count: Integer): TArray<T>;
var
i: Integer;
begin
SetLength(Result, Count);
for i := 0 to high(Result) do begin
Result[i] := Source[i+Index];
end;
end;
class function TArray.Copy<T>(const Source: array of T): TArray<T>;
var
i: Integer;
begin
SetLength(Result, Length(Source));
for i := 0 to high(Result) do begin
Result[i] := Source[i];
end;
end;
请注意,以上所有内容都要求您停止使用 array of TMyType
,而是开始使用通用动态数组 TArray<TMyType>
。
在你的情况下,你过于复杂了。替换:
SetLength(myData[Idx].WorkNames,Length(tmpNames)); // <- prepare to copy
for i := 0 to High(tmpNames) do // <- copy
myData[Idx].WorkNames[i]:=tmpNames[i];
SetLength(tmpNames,0); // <- clear tmp array
与:
myData[Idx].WorkNames := tmpNames;
tmpNames := nil;
如果您准备让 tmpNames
简单地离开范围,那么您可以使用一行:
myData[Idx].WorkNames := tmpNames;
虽然,如果 tmpNames
被重新用于不同的数组,则需要 nil
赋值。
话又说回来,据我从问题中的代码中可以看出,您根本不需要临时数组。为什么不直接对长期存在的数据结构进行操作。
只有在赋值的源和目标是赋值兼容的情况下,这些数组赋值才被允许。您的类型不是因为您使用了不同的类型。切换到 TArray<T>
以避免这种情况。有关更多信息,请参阅此问题:Why are two seemingly identical dynamic array types deemed not assignment compatible?
记住动态数组是引用类型。在这里显示的用法中,您需要做的就是复制引用。您只需要实际数组的一个实例。所以根本不需要复制。
我有一个进程将数据读入 150 多个临时数组,处理数据并将临时数组中的数据复制到工作数组中。工作数组在一个全局数组中,因此我可以导入多个数据,这意味着我可以重复相同的过程多达 100 次,并最终得到一组包含 100 倍工作数据的大数组,我可以使用、比较和做一些事情。
我有 150 多个数组,所以 150 次:
// for each array
SetLength(myData[Idx].WorkNames,Length(tmpNames)); // <- prepare to copy
for i := 0 to High(tmpNames) do // <- copy
myData[Idx].WorkNames[i]:=tmpNames[i];
SetLength(tmpNames,0); // <- clear tmp array
每个数组 4 行代码 - 150x4 = 600 loc + 初始 + 空行 - 大约 900 loc.
以下是我所做的示例:
type
TName = record
NameID:integer;
Description:string;
end;
TItem = record
ItemID:integer;
Description:string;
Active:boolean;
end;
TWorkData = record
WorkDataType:string;
WorkNames:array of TName;
WorkItems:array of TItem;
end;
var
AllWorkData:array of TWorkData; // <- global array that has all work data - up to 100x sets of work data
tmpNames:array of TName; // <- tmp arrays before saving to work array
tmpItems:array of TItem; //
procedure TForm1.Button1Click(Sender: TObject);
var i,Idx:integer;
begin
// 1. read data into tmp arrays
ReadDataIntoTmpArrays;
ProcessTmpData;
// 2. copy tmp arrays into work data
Idx:=GetWorkDataIdx; // <- work data sequence number; start with 0
AllWorkData[Idx].WorkDataType:=GetWorkDataName(Idx);
SetLength(AllWorkData[Idx].WorkNames,Length(tmpNames));
SetLength(AllWorkData[Idx].WorkItems,Length(tmpItems));
for i := 0 to High(tmpNames) do
AllWorkData[Idx].WorkNames[i]:=tmpNames[i];
for i := 0 to High(tmpItems) do
AllWorkData[Idx].WorkItems[i]:=tmpItems[i];
// 3. clear tmp arrays
SetLength(tmpNames,0);
SetLength(tmpItems,0);
end;
问题:有什么我可以做的更容易维护,重构代码吗?
如果你真的想复制,那么使用泛型来实现。您可以从 System.Generics.Collections
中声明的静态 class 方法的 TArray
class 派生。例如:
type
TArray = class(Generics.Collections.TArray)
public
class function Copy<T>(const Source: array of T; Index, Count: Integer): TArray<T>; overload; static;
class function Copy<T>(const Source: array of T): TArray<T>; overload; static;
end;
....
class function TArray.Copy<T>(const Source: array of T; Index, Count: Integer): TArray<T>;
var
i: Integer;
begin
SetLength(Result, Count);
for i := 0 to high(Result) do begin
Result[i] := Source[i+Index];
end;
end;
class function TArray.Copy<T>(const Source: array of T): TArray<T>;
var
i: Integer;
begin
SetLength(Result, Length(Source));
for i := 0 to high(Result) do begin
Result[i] := Source[i];
end;
end;
请注意,以上所有内容都要求您停止使用 array of TMyType
,而是开始使用通用动态数组 TArray<TMyType>
。
在你的情况下,你过于复杂了。替换:
SetLength(myData[Idx].WorkNames,Length(tmpNames)); // <- prepare to copy
for i := 0 to High(tmpNames) do // <- copy
myData[Idx].WorkNames[i]:=tmpNames[i];
SetLength(tmpNames,0); // <- clear tmp array
与:
myData[Idx].WorkNames := tmpNames;
tmpNames := nil;
如果您准备让 tmpNames
简单地离开范围,那么您可以使用一行:
myData[Idx].WorkNames := tmpNames;
虽然,如果 tmpNames
被重新用于不同的数组,则需要 nil
赋值。
话又说回来,据我从问题中的代码中可以看出,您根本不需要临时数组。为什么不直接对长期存在的数据结构进行操作。
只有在赋值的源和目标是赋值兼容的情况下,这些数组赋值才被允许。您的类型不是因为您使用了不同的类型。切换到 TArray<T>
以避免这种情况。有关更多信息,请参阅此问题:Why are two seemingly identical dynamic array types deemed not assignment compatible?
记住动态数组是引用类型。在这里显示的用法中,您需要做的就是复制引用。您只需要实际数组的一个实例。所以根本不需要复制。