【问题标题】:Delphi 2010+ and "Left side cannot be assigned to" in read-only records: can this be disabled?Delphi 2010+和只读记录中的“左侧不能分配给”:可以禁用吗?
【发布时间】:2011-07-01 14:53:50
【问题描述】:

我知道what changed。我知道为什么。但是..

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;

这在 Delphi 2010 之前完美运行。这是一种非常有用的技术,用于进行接受大量参数但通常只需要两个或三个参数的调用。但绝不是相同的。

现在它给出了......你猜怎么着?

E2064: Left side cannot be assigned to.

不能以某种方式禁用这种有用的新行为吗?关于如何修改模式以使其正常工作的任何想法?

因为严重的是,无缘无故失去了如此方便的技术(并重写了一堆代码)......

【问题讨论】:

  • 我很惊讶它曾经工作过。
  • @David Hefferman:为什么?这是一个完全有效的技巧。没有黑客攻击,只有语言功能。
  • 它被删除了,因为它从一开始就不应该被允许。
  • 我没有将此问题与what changed 参考进行比较。特别是由于属性总是按照该主题描述的方式运行,因此它从未“改变”。

标签: delphi delphi-2010


【解决方案1】:

我觉得这有点令人惊讶,但既然你说它确实有效,我相信你是对的。我猜这个改变是在没有考虑记录方法的情况下做出的。如果没有调用方法的能力,那么这种构造将毫无意义。

无论如何,编译器不会让你摆脱这个问题,所以你必须这样做:

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;

【讨论】:

  • 是的,我也立即尝试过,它有效。但这会扼杀一半的美丽……真的没有其他办法了吗?
  • 你可以使用一个类的实例,但你必须释放它
  • 是的,它比(可能是堆栈分配的!)返回记录要慢。
  • 这里没有美女可以杀人。以前依赖于损坏的编译器,这都是纯粹的丑陋黑客。编译器现已修复。
  • @Warren P:不,不是。黑客正在针对编译器工作。这个技巧依赖于记录在案的预期行为。说你想说的,虽然编译器开发者没有想到这种用法,但它是合法的。意外使用!= 黑客攻击。事实上,试图将你可以做的事情限制在明确声明你可以做的事情上,这正在扼杀原本丰富的语言。
【解决方案2】:

我……我想我做到了

我希望 Delphi 开发人员看看他们让他们的程序员做什么!

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;

这可行,保留了旧版本的所有优点(速度、简单、调用开销小),但这种方法没有任何隐藏的问题吗?

【讨论】:

  • 使用此解决方案,您无论如何都必须更改 CallMaker 的所有用法。所以我建议你一直使用像@David suggested 这样的显式变量。您不会注意到任何开销并摆脱 with 引入的歧义。
  • 这看起来简直太糟糕了。你想达到什么目的?
  • 你为什么不把所有的东西都传递给记录中的函数呢?为什么要对抗语言?
  • 这依赖于CallMaker 返回的临时值将持续到封闭with 块的末尾的假设。找到某处记录的保证,我认为你有你的解决方案。如果没有这个保证,你的情况不会比 Delphi 2010 之前更好,这取决于编译器的技术性。
  • @Rob Kennedy:不幸的是,你是对的。不依赖实现的唯一方法可能是“使用 CallMaker with This^ begin ... end; Call; end”。这样 Delphi 在 Call() 执行之前无法释放 CallMaker。
猜你喜欢
  • 2010-10-11
  • 1970-01-01
  • 2011-05-08
  • 1970-01-01
  • 1970-01-01
  • 2020-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多