Search code examples
delphidelphi-xe8generic-collections

System.Generics.Collections differences in XE8 compared to other Delphi versions


I have the following unit I have used for some Delphi versions. But now I am testing XE8 and I get an error in

function THolydayList.TEnglishNameCollection.ToArray: TArray<string>;
begin
  Result := ToArrayImpl(fList.Count);
end;

I think is has to do with the fact that System.Generics.Collections is rewritten in XE8. But pressed for time I haven't had the opportunity to look into that. My question is: has anybody looked into this and can guide me in a direction as of what to look for.

An example of my use of this unit could be the following where I update a table with dates related to an employer for a given year

procedure TsCalendarYearHolyday(aEmployer: string; aYear: integer);
var
  STDDato: TStdDato;
  HolyDay : THolyDay;
  Query: TUniQuery;
begin
  Query := frmUniConn.CreateQuery;
  STDDato := TStdDato.Create;
  STDDato.Year := aYear;
  STDDato.Country := Denmark;
  STDDato.MarkSunday := False;
  STDDato.Language := hdNative;
  STDDato.MakeHoliDays(0);
  try
    Query.SQL.Clear;
    Query.SQL.Add('UPDATE ' + TableTsCalendarYear);
    Query.SQL.Add('   SET flddayspecial = :flddayspecial');
    Query.SQL.Add('      ,flddaydesc = :flddaydesc');
    Query.SQL.Add('      ,flddaynormal = :flddaynormal');
    Query.SQL.Add(' WHERE (flddate = :flddate)');
    Query.SQL.Add('   AND (fldemployer = :fldemployer)');
    for HolyDay in STDDato.Liste do
      begin
        try
          Query.ParamByName('flddayspecial').AsBoolean := True;
          Query.ParamByName('flddaydesc').AsString := Holyday.NativeName;
          Query.ParamByName('flddaynormal').AsFloat := 0;
          Query.ParamByName('flddate').AsDate := HolyDay.Date;
          Query.ParamByName('fldemployer').AsString := aEmployer;
          Query.Execute;
        except
          on E: exception do
            Logfile.Error('U_TsCalendars.TsCalendarYearHolyday: ' + E.Message);
        end;
      end;
  finally
    FreeAndNil(STDDato);
    Query.Free;
  end;
end;

I added a link since this is the whole unit


Solution

  • It's a little frustrating that you say "I get an error" without stating what that error is. Run time error? Compile time error? Please be precise about errors. In my experience, when people don't say what the error is it is often because they didn't read the error message closely and attempt to understand it. That is usually a mistake. So my biggest piece of advice to you is to pay closer attention to such details and hopefully that will help you solve such problems for yourself in the future.


    However, since it is clear enough that the call to ToArrayImpl will fail to compile because ToArrayImpl is a private member, we can work it out.

    Clearly you cannot call ToArrayImpl because it is private. And the only way we can work out what you need is to read the code in your off site link. Again, not ideal. This code will compile and behave as you intend.

    function THolydayList.TEnglishNameCollection.ToArray: TArray<string>;
    var
      i: Integer;
    begin
      SetLength(Result, fList.Count);
      for i := 0 to fList.Count-1 do
        Result[i] := fList[i].EnglishName;
    end;
    

    Whether or not this is the best solution to your problem, I am not prepared to say.


    Now, one might wonder how your code ever worked. This was due to compiler bugs in older versions. The ToArrayImpl method has always been private. Older versions of the compiler failed to correctly enforce that visibility and allowed your class to call ToArrayImpl when it was not meant to.

    This code:

    uses
      Generics.Collections;
    
    type
      TMyEnumerable<TObject> = class(TEnumerable<TObject>)
        procedure Foo;
      end;
    
    procedure TMyEnumerable<TObject>.Foo;
    begin
      ToArrayImpl(0);
    end;
    

    compiles in XE7 but not XE8. Clearly the older compilers were broken.