开发者

Delphi 2010+ and "Left side cannot be assigned to" in read-only records: can this be disabled?

I know what changed. I know why. But..

TComplicatedCallMaker = record
    Param1: TRecordType;
    Param2: TRecordType;
    {...}
    Param15: TRecordType;
   开发者_运维问答 procedure Call;
end;

function ComplicatedCall: TComplicatedCallMaker;
begin
 { Fill default param values }
end;

procedure DoingSomeWorkHere;
begin
  with ComplicatedCall do begin
    Param7 := Value7;
    Param12 := Value12;
    Call;
  end;
end;

This has perfectly worked before Delphi 2010. An extremely useful technique for making calls which accept a load of parameters but usually only need two or three. Never the same ones though.

And now it gives... guess what?

E2064: Left side cannot be assigned to.

Can't this helpful new behavior be disabled somehow? Any ideas on how to modify the pattern so it works?

Because seriously, losing such a handy technique (and rewriting a bunch of code) for no apparent reason...


I find it a little surprising that this ever worked but since you say it did I'm sure you are right. I'd guess the change was made without consideration for record methods. Without the ability to call methods then this construct would be rather pointless.

Anyway, the compiler isn't going to let you off the hook on this one so you'll have to do this:

type
  TRecordType = record end;
  TComplicatedCallMaker = record
    Param1: TRecordType;
    procedure Call;
  end;

function ComplicatedCall: TComplicatedCallMaker;
begin
 { Fill default param values }
end;

procedure DoingSomeWorkHere(const Value: TRecordType);
var
  CallMaker: TComplicatedCallMaker;
begin
  CallMaker := ComplicatedCall;
  with CallMaker do begin
    Param1 := Value;
    Call;
  end;
end;


I... think I did it

I hope Delphi developers see what they make their programmers do!

type
  PCallMaker = ^TCallMaker;
  TCallMaker = record
    Param1: integer;
    Param2: integer;
    function This: PCallMaker; inline;
    procedure Call; inline;
  end;

function TCallMaker.This: PCallMaker;
begin
  Result := @Self;
 { Record functions HAVE to have correct self-pointer,
  or they wouldn’t be able to modify data. }
end;

procedure TCallMaker.Call;
begin
  writeln(Param1, ' ', Param2);
end;

function CallMaker: TCallMaker; inline
begin
  Result.Param1 := 0;
  Result.Param2 := 0;
end;

procedure DoingSomeWorkHere;
var cm: TCallMaker;
begin
 {Test the assumption that cm is consistent}
  cm := CallMaker;
  if cm.This <> @cm then
    raise Exception.Create('This wasn''t our lucky day.');

 {Make a call}
  with CallMaker.This^ do begin
    Param1 := 100;
    Param2 := 500;
    Call;
  end;
end;

This works, preserves all the good points of the old version (speed, simplicity, small call overhead) but aren't there any hidden problems with this approach?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜