在 Delphi 10.2 中写入 MemoryStream 有何变化?
How writing to MemoryStream has changed in Delphi 10.2?
我正在将我的代码从 10.1 移植到 10.2,这给了我错误:
procedure TForm4.FormCreate(Sender: TObject);
const
CFourBytes: array[0..3] of Byte = (1, 2, 3, 4);
var
LStream: TMemoryStream;
LBuffer: array of Byte;
begin
SetLength(LBuffer, 4);
LStream := TMemoryStream.Create;
LStream.Write(@CFourBytes[0], 4); // E2036 Variable required
LStream.Position := 0;
LStream.ReadData(@LBuffer[0], 4);
end;
我不得不将违规行更改为 LStream.Write(CFourBytes[0], 4);
发生了什么变化?我一直做错了吗?
也许什么都没有改变。
TStream.Write/Read
方法总是使用无类型的 const/var
参数 const Buffer
(help) 并且使用变量地址是错误的(因为方法(确切地说是编译器)发现变量本身的地址)。
您可能不小心将这些方法与使用 typed 参数和 [=19= 之一的 Read/WriteData 混淆了] 获取 Pointer
类型参数。
此处 ReadData
实现取消引用此指针并在内部使用 Read
(Read
依次调用 Move
,最后一个例程再次获取缓冲区地址 :))
您问题中的代码确实可以在旧版本中编译,但不应该编译。在 10.2 中看到的行为是正确的。
旧版本中发生的事情很奇怪。编译器在 TStream
:
中选择此重载
function Write(const Buffer: TBytes; Count: Longint): Longint; overload;
这尤其令人震惊,因为传递给此方法的是静态数组的地址 CFourBytes
。这绝对不是 TBytes
对象。
现在正好有一个TBytes
变量是数组第一个元素的地址。 TMemoryStream.Write
的 TBytes
覆盖中没有任何内容指向那个伪造的 TBytes
对象的 Length()
。所以你的代码恰好按预期工作。这很明显是一个已修复的编译器错误。
你的密码一直都是坏的,你只是,直到现在,侥幸逃过一劫。你应该修复你的代码。像这样:
LStream := TMemoryStream.Create;
try
LStream.WriteBuffer(CFourBytes, SizeOf(CFourBytes));
SetLength(LBuffer, LStream.Size);
LStream.Position := 0;
LStream.ReadBuffer(LBuffer[0], LStream.Size);
finally
LStream.Free;
end;
请注意,我使用的是 WriteBuffer
和 ReadBuffer
而不是 Write
和 Read
。这些是与 TStream
一起使用的首选方法。原因是它们执行错误检查并在出现错误时引发异常,这与 Write
和 Read
.
不同
我正在将我的代码从 10.1 移植到 10.2,这给了我错误:
procedure TForm4.FormCreate(Sender: TObject);
const
CFourBytes: array[0..3] of Byte = (1, 2, 3, 4);
var
LStream: TMemoryStream;
LBuffer: array of Byte;
begin
SetLength(LBuffer, 4);
LStream := TMemoryStream.Create;
LStream.Write(@CFourBytes[0], 4); // E2036 Variable required
LStream.Position := 0;
LStream.ReadData(@LBuffer[0], 4);
end;
我不得不将违规行更改为 LStream.Write(CFourBytes[0], 4);
发生了什么变化?我一直做错了吗?
也许什么都没有改变。
TStream.Write/Read
方法总是使用无类型的 const/var
参数 const Buffer
(help) 并且使用变量地址是错误的(因为方法(确切地说是编译器)发现变量本身的地址)。
您可能不小心将这些方法与使用 typed 参数和 [=19= 之一的 Read/WriteData 混淆了] 获取 Pointer
类型参数。
此处 ReadData
实现取消引用此指针并在内部使用 Read
(Read
依次调用 Move
,最后一个例程再次获取缓冲区地址 :))
您问题中的代码确实可以在旧版本中编译,但不应该编译。在 10.2 中看到的行为是正确的。
旧版本中发生的事情很奇怪。编译器在 TStream
:
function Write(const Buffer: TBytes; Count: Longint): Longint; overload;
这尤其令人震惊,因为传递给此方法的是静态数组的地址 CFourBytes
。这绝对不是 TBytes
对象。
现在正好有一个TBytes
变量是数组第一个元素的地址。 TMemoryStream.Write
的 TBytes
覆盖中没有任何内容指向那个伪造的 TBytes
对象的 Length()
。所以你的代码恰好按预期工作。这很明显是一个已修复的编译器错误。
你的密码一直都是坏的,你只是,直到现在,侥幸逃过一劫。你应该修复你的代码。像这样:
LStream := TMemoryStream.Create;
try
LStream.WriteBuffer(CFourBytes, SizeOf(CFourBytes));
SetLength(LBuffer, LStream.Size);
LStream.Position := 0;
LStream.ReadBuffer(LBuffer[0], LStream.Size);
finally
LStream.Free;
end;
请注意,我使用的是 WriteBuffer
和 ReadBuffer
而不是 Write
和 Read
。这些是与 TStream
一起使用的首选方法。原因是它们执行错误检查并在出现错误时引发异常,这与 Write
和 Read
.