Delphi TStringList作为对象字段

kknvjkwl  于 2023-02-04  发布在  其他
关注(0)|答案(1)|浏览(152)

这个问题以前可能已经问过很多次了,但如果是这样的话,我一辈子也找不到答案,所以如果是这样的话,我提前道歉。
我在一个 Delphi 单元中有一个对象,我正在整理来自一个源的TEmployerData类型的雇主列表,如下所示。
一旦我整理了一份雇主名单,我将从其他来源收集属于每个雇主的雇员和工资单数据。

unit EmployerObjUnit;

interface

uses
  Classes, SysUtils, Variants,Types, Generics.Collections, Generics.Defaults, EmployeeObjUnit, PayObjUnit;

type
  TEmployerData = class
  private
    FErID: string;
    FErName: string;
    FErAccsRef: string;
    FErPAYE: string;
    FErAddr1: string;
    FErAddr2: string;
    FErAddr3: string;
    FErAddr4: string;
    FErPostCd: string;
    FErPath: string;
    FErEesList: TObjectList<TFPSEmployee>;
    FErPayList: TObjectList<TFPSPayment>;
    FErYears: TStringList;
    procedure SetErID (const Value: string);
    procedure SetErName (const Value: string);
    procedure SetErAccsRef (const Value: string);
    procedure SetErPAYE (const Value: string);
    procedure SetErAddr1 (const Value: string);
    procedure SetErAddr2 (const Value: string);
    procedure SetErAddr3 (const Value: string);
    procedure SetErAddr4 (const Value: string);
    procedure SetErPostCd (const Value: string);
    procedure SetErPath (const Value: string);
    constructor Create; overload;
  published
    property ErID:string read FErID write SetErID;
    property ErName:string read FErName write SetErName;
    property ErAccsRef:string read FErAccsRef write SetErAccsRef;
    property ErPAYE:string read FErPAYE write SetErPAYE;
    property ErAddr1:string read FErAddr1 write SetErAddr1;
    property ErAddr2:string read FErAddr2 write SetErAddr2;
    property ErAddr3:string read FErAddr3 write SetErAddr3;
    property ErAddr4:string read FErAddr4 write SetErAddr4;
    property ErPostCd:string read FErPostCd write SetErPostCd;
    property ErPath: string read  FErPath write SetErPath;
    property ErEesList: TObjectList<TFPSEmployee> read FErEesList;
    property ErPayList: TObjectList<TFPSPayment> read FErPayList;
    property ErYears: TStringList read FErYears;
  public
    procedure AddEmployee(const FPSEmployee: TFPSEmployee);
    procedure AddPayslip(const FPSPayslip: TFPSPayment);
    procedure AddYear(const Year: string);
end;

到目前为止一切顺利。
我想将雇员和工资单数据存储在ErEEsListErPayList ObjectList中,并将与这些数据相关的年份存储在ErYears StringList中。
类代码的其余部分为:

constructor TEmployerData.Create;
begin
  inherited;
  FErEesList:=TObjectList<TFPSEmployee>.Create(True);
  FErPayList:=TObjectList<TFPSPayment>.Create(True);
  FErYears:=TStringList.Create;
end;

procedure TEmployerData.SetErAccsRef(const Value: string);
begin

// all the other setters are in here

end;

procedure TEmployerData.AddEmployee(const FPSEmployee: TFPSEmployee);
var
  IsDupe: Boolean;
  i: integer;
begin
  if FErEesList.Count=0 then
    FErEesList.Add(FPSEmployee)
  else
    begin
      IsDupe:=False;
      for i := 0 to FErEesList.Count-1 do
        begin
          if (FErEesList[i].PayID=FPSEmployee.PayID)
          AND (FErEesList[i].AccountsRef=FPSEmployee.AccountsRef)
          AND (FErEesList[i].TaxYear=FPSEmployee.TaxYear) then
            IsDupe:=True;
        end;
      if IsDupe=False then
        FErEesList.Add(FPSEmployee);
      if IsDupe=True then
        FPSEmployee.Free;
    end;
  FErEesList.Sort(TComparer<TFPSEmployee>.Construct(
    function(const A, B :TFPSEmployee): integer
      begin
        if A.TaxYear=B.TaxYear then
          Result:=0
        else if A.TaxYear<B.TaxYear then
          Result:=-1
        else
          Result:=1;
      end
  ));
end;

procedure TEmployerData.AddPayslip(const FPSPayslip: TFPSPayment);
begin
  FErPayList.Add(FPSPayslip);
  FErPayList.Sort(TComparer<TFPSPayment>.Construct(
    function(const A, B :TFPSPayment): integer
      begin
        if A.TaxYear=B.TaxYear then
          Result:=0
        else if A.TaxYear<B.TaxYear then
          Result:=-1
        else
          Result:=1;
      end
  ));
end;

procedure TEmployerData.AddYear(const Year: string);
var
  i: integer;
  GotYr: Boolean;
begin
  GotYr:=False;
  if FErYears.Count=0 then
    FErYears.Add(Year)
  else
    begin
      for i := 0 to FErYears.Count-1 do
        begin
          if Year=FErYears[i] then
            GotYr:=True;
        end;
      if GotYr=False then
        FErYears.Add(Year);
    end;
end;

end.

现在,我可以毫无问题地整理我的雇主列表了。我可以得到我需要的每个雇员和工资单的信息,但是当我试图用AddEmployee()AddYear()写任何东西时,我总是得到访问违规错误(甚至还没有到AddPayslip()!)。不幸的是,我不够流利,无法找出原因。
上述类用于一个Form单元。
ErsObjList: TObjectList<TEmployerData>;
以上声明在表单单元的Private节中,创建表单时创建,关闭表单时释放。
然后用它来填充ErsObjList

procedure TGetXMLForm.Button1Click(Sender: TObject);
var
  //more XML variables
  ANode, BNode, CNode: IXMLNode;
  NumDir: string;
  Employer: TEmployerData;
begin
  ErStream:=TFileStream.Create('Employer List.xml', fmOpenRead);
  // load of xml setup
  try
    if Length(XList)>0 then
      begin
        for i := 0 to Length(XList)-1 do
          begin
            SetLength(FPSList, 0);
            FPSList:=TDirectory.GetFiles(XList[i], 'FPS*.xml', TSearchOption.soAllDirectories);
            try
              if Length(FPSList)>0 then
                begin
                  // scan through ErListXML for the corresponding number
                  ErNodes:=ErListXML.DocumentElement.ChildNodes;
                  if ErNodes.Count>0 then
                    begin
                      for x:= 0 to Ernodes.Count-1 do
                        begin
                          ANode:=ErNodes[x].ChildNodes.FindNode('Number');
                          if StrToInt(ANode.Text)=StrToInt(NumDir) then
                            begin
                              // create an employer obj from ErListXML
                              Employer:=TEmployerData.Create;
                              Employer.ErID:=ANode.Text;
                              Employer.ErName:=ErNodes[x].ChildNodes.FindNode('Name').Text;
                              // and so on until
                              Employer.ErPath:=XList[i];
                              ErsObjList.Add(Employer);
                            end;
                        end;
                    end;
                end;
            except
              ShowMessage('Exception class name :- '+E.ClassName);
              Exit;
            end;
          end;
      end;
    ErListXML.Free;
  except
    ShowMessage('Error reading Employer List xml file');
  end;
end;

Button 1从一个源获取我的雇主数据,并构建一个ObjectList(ErsObjList),没有出现任何问题。
然后,当单击Button2时,我将使用以下命令:

procedure TGetXMLForm.Button2Click(Sender: TObject);
var
  i: integer;
  FPSStream: TStream;
begin
  for i := 0 to ErsObjList.Count-1 do
    begin
      GetPayDetails(ErsObjList[i]);
      WriteData;
    end;
end;

这反过来又触发了一个更完整的版本(为了可读性,我只是删除了一些基本代码--没有任何会影响这个问题的代码):

procedure TGetXMLForm.GetRTIDetails(const Employer: TEmployerData);
var
  FpsList: TStringDynArray;
  // other items
  TaxYear: string;
  Employee: TFPSEmployee;
  Payslip: TFPSPayment;
  DateConInf: TFormatSettings;
  TaxCd: string;
begin
  SetLength(FpsList, 0);
  FpsList:=TDirectory.GetFiles(Employer.ErPath, 'FPS*.xml', TSearchOption.soAllDirectories);
  if Length(FpsList)>0 then
    begin
      try
        for i := 0 to Length(FpsList)-1 do
          begin
        // loading some data from XML files
            TaxYear:=CNode.ChildNodes.FindNode('RelatedYear').Text;
            Employer.AddYear(TaxYear);
        // my code then triggers an AV in the "AddYear" procedure

这就是出错的地方。它确实用正确的TaxYear值调用了过程AddYear()。它没有标记任何编译错误。我将感谢任何帮助。

  • 已编辑 *
jyztefdp

jyztefdp1#

对于我尝试循环的所有代码,答案应该是非常明显的。但是正如我所说的,我没有足够的经验来知道。TEmployerDataconstructor声明在错误的位置,所以这些

FErEesList: TObjectList<TFPSEmployee>;
    FErPayList: TObjectList<TFPSPayment>;
    FErYears: TStringList;

在创建Employer对象时未正确初始化。这会导致运行时AV。

相关问题