Delphi E2009不兼容类型:';参数列表不同';

Delphi E2009不兼容类型:';参数列表不同';,delphi,compiler-errors,delphi-xe2,Delphi,Compiler Errors,Delphi Xe2,我得到以下错误: E2009不兼容类型:“参数列表不同” 但是我不同意,从定义上看,我看不出有什么不同 以下是记录定义: type TFastDiv = record private ... DivideFunction: function (const Buffer: TFastDiv; x: integer): integer; 这是我想分配的Mod函数: function dividefixedi32(const Buffer: TFastDiv; x: int

我得到以下错误:

E2009不兼容类型:“参数列表不同”

但是我不同意,从定义上看,我看不出有什么不同

以下是记录定义:

type
  TFastDiv = record
  private
    ...
    DivideFunction: function (const Buffer: TFastDiv; x: integer): integer;
这是我想分配的Mod函数:

function dividefixedi32(const Buffer: TFastDiv; x: integer): integer;
asm
以下分配将发出错误:

class operator TFastDiv.Implicit(a: integer): TFastDiv;
begin
  if (a = 0) then begin 
    raise EDivByZero.Create('Setting a zero divider is a division by zero error') 
      at ReturnAddress; 
  end;
  Result.FSign:= Math.sign(a);
  case Result.FSign of
    -1: begin
      SetDivisorI32(Result, a);
      Result.DivideFunction:= dividefixedi32;  <<-- error E2009
变通办法 如果我这样更改代码:

记录定义

type
  TFastDiv = record
  private
    ...
    DivideFunction: function (var Buffer: TFastDiv; x: cardinal): cardinal;
                              ^^^
功能定义

function dividefixedu32(var Buffer: TFastDiv; x: Cardinal): cardinal; // unsigned
asm  //                 ^^^  
这个问题也解决了


请注意,如果我将
var
更改回
const
,问题就会再次出现

首先,一些一般性的建议。你的SSCCE很差。它既不短也不完备。这其实是相当重要的。经常使演示代码尽可能短有助于理解问题。这里的情况绝对如此

以下是我对SSCCE的看法:

program soq19147523_version1;

type
  TRecord = record
    data: Integer;
    proc: procedure(const rec: TRecord);
  end;

procedure myproc(const rec: TRecord);
begin
end;

procedure foo;
var
  rec: TRecord;
begin
  rec.proc := myproc; // fail, E2009
end;

begin
end.
这无法使用E2009编译。您可以通过多种方式使其编译。例如,删除
数据
成员会导致编译成功

program soq19147523_version2;

type
  TRecord = record
    proc: procedure(const rec: TRecord);
  end;

procedure myproc(const rec: TRecord);
begin
end;

procedure foo;
var
  rec: TRecord;
begin
  rec.proc := myproc; // compiles
end;

begin
end.
在XE3中,可以通过向过程类型的参数添加
[ref]
属性来编译它。明确地说,这在XE3中编译:

program soq19147523_version3;

type
  TRecord = record
    data: Integer;
    proc: procedure(const [ref] rec: TRecord);
  end;

procedure myproc(const [ref] rec: TRecord);
begin
end;

procedure foo;
var
  rec: TRecord;
begin
  rec.proc := myproc; // compiles in XE3, no [ref] in XE2
end;

begin
end.
这为我们提供了编译器正在做什么的有力线索。未修饰的
const
记录参数通过值或引用传递。如果记录足够小,可以放入寄存器,则将按值传递

编译器处理记录时,尚未完全确定记录的大小。我猜在编译器内部有一个包含记录大小的变量。在记录的声明完成之前,我假定这个大小变量为零。因此,编译器决定记录类型的
const
参数将通过寄存器中的值进行传递。当遇到过程
myproc
时,记录的真实大小是已知的。它不适合寄存器,因此编译器会识别不匹配。记录中的类型通过值接收其参数,但为赋值提供的类型通过引用传递参数

program soq19147523_version4;

type
  TRecord = record
    data: Integer;
    proc: procedure(const rec{: TRecord});
  end;

procedure myproc(const rec{: TRecord});
begin
  Writeln(TRecord(rec).data);
end;

procedure foo;
var
  rec: TRecord;
begin
  rec.proc := myproc;
end;

begin
end.
实际上,您可以从
myproc
的声明中删除
[ref]
,程序仍然可以编译

这也解释了为什么您发现使用
var
参数可以成功编译。这显然会强制通过引用传递参数

program soq19147523_version4;

type
  TRecord = record
    data: Integer;
    proc: procedure(const rec{: TRecord});
  end;

procedure myproc(const rec{: TRecord});
begin
  Writeln(TRecord(rec).data);
end;

procedure foo;
var
  rec: TRecord;
begin
  rec.proc := myproc;
end;

begin
end.
如果您可以移动到XE3或更高版本,那么解决方案是显而易见的:使用
[ref]
强制编译器操作

如果无法移动到XE3,那么非类型化的
const
参数可能是最佳解决方案。这还强制编译器通过引用传递参数

program soq19147523_version4;

type
  TRecord = record
    data: Integer;
    proc: procedure(const rec{: TRecord});
  end;

procedure myproc(const rec{: TRecord});
begin
  Writeln(TRecord(rec).data);
end;

procedure foo;
var
  rec: TRecord;
begin
  rec.proc := myproc;
end;

begin
end.

经常阅读我在这里发表的关于堆栈溢出的文章的读者会知道,我是值类型记录上运算符重载的大力支持者。我广泛地使用了这个特性,它产生了高效且可读性高的代码。但是,当您开始努力使用更复杂和相互依赖的类型时,设计和实现就会崩溃

这个问题中突出的缺陷就是一个很好的例子。期望编译器能够处理这一点并不少见。期望类型能够引用自身是非常合理的

实现让程序员失望的另一个例子是,您希望在该记录中放入记录类型的
const
。例如,考虑这种类型:

type
  TComplex = record
  public
    R, I: Double;
  const
    Zero: TComplex = (R: 0.0, I: 0.0);
  end;
由于E2086类型“TComplex”尚未完全定义,因此在声明
Zero
时无法编译

另一个限制是A型不能引用B型,反之亦然。我们可以对类进行前向声明,但不能对记录进行前向声明。我知道需要修改编译器实现以支持这一点,但这当然是可以实现的

还有更多。为什么不允许对记录进行继承?我不想要多态性,我只想继承记录的数据成员和方法。我甚至不需要你在课堂上表现出来的那种行为。也就是说,如果
tDelivedRecord
不是
TBaseRecord
,我并不介意。我只想继承成员和函数以避免重复


可悲的是,在我看来,这是一个已经完成90%的功能,只是缺少了完成它所需的温柔和关爱。

在我尝试它时,它对我来说很好。如果没有看到,我的猜测可能是作用域中可能有多个相互冲突的
TFastDiv
类型。不,只有TFastDiv记录定义(通过在文件中查找确认)。@RemyLebeau,我添加了一个SSCCET,它完美地解释了这一点。雷米的回答让我偏离了正轨,认为区别在于“程序”和“单位”,而实际上是“数据成员”和“无数据成员”。您刚刚完成了我的一天:-|),谢谢。似乎还可以处理非类型作业
rec.proc:=@myproc
({$T-})。编译器可能不太喜欢它,偶尔会导致内部错误。@SertacAkyuz我想知道编译器是按ref传递还是按值传递that@SertacAkyuz这样生成的代码似乎很好。这似乎是个不错的解决办法。相信您能找到
@
和过程类型的良好用途,嗯?!!-;)