【发布时间】:2011-10-06 12:07:46
【问题描述】:
我正在构建一个非常粗糙的 GUI 来建模映射器,它基本上遍历表单上的所有 TEdit 和 TMemo 字段,提取文本并将此文本设置在数据模型对象中。 (解决方案依赖于我公认的脆弱的“约定优于配置”方法,仅匹配数据模型中与表单中的字段具有相同名称的属性。)
免责声明:对于臃肿的代码示例感到抱歉。如下:
表格。
{ Standard interface section above this line }
type
TfrmMain = class(TForm)
StringField1: TEdit;
StringField2: TMemo;
{ Other fields and procedures dropped for brevity }
private
procedure FillGUIFields();
end;
数据模型。
TDataModel = class(TObject)
private
FStringField1: string;
FStringField2: string;
{ Getters and setters dropped for brevity }
public
property StringField1: string read GetFStringField1 write SetFStringField1;
property StringField2: string read GetFStringField2 write SetFStringField2;
end;
实施。
const
MAX_RUNS = 100;
procedure GUIToData(var AObject: TObject; const Form: TForm);
var
c: TRTTIContext;
t: TRTTIType;
prop: TRTTIProperty;
Component: TComponent;
Text: string;
i: integer;
begin
c := TRTTIContext.Create();
t := c.GetType(AObject.ClassType);
for prop in t.GetProperties do begin
Component := Form.FindComponent(prop.Name); // Naive "conv. over conf." matching
if (Component <> nil) then begin
if (Component is TEdit) then prop.SetValue(AObject, TValue.FromVariant(TEdit (Component).Text));
if (Component is TMemo) then prop.SetValue(AObject, TValue.FromVariant(TMemo(Component).Text));
end;
end;
c.Free();
end;
procedure TfrmMain.btnFetchToModelClick(Sender: TObject);
var
Data: TDataModel;
i: integer;
NumberOfExceptions: integer;
begin
NumberOfExceptions := 0;
for i := 0 to MAX_RUNS - 1 do begin
try
FillGUIFields();
Data := TDataModel.Create();
GUIToData(TObject(Data), self);
Data.Free();
except on E: EAccessViolation do
begin
Inc(NumberOfExceptions);
end;
end;
end;
MessageDlg('Number of runs: ' + IntToStr(MAX_RUNS) + #13#10 +
'Number of exceptions: ' + IntToStr(NumberOfExceptions), mtInformation, [mbOk], 0);
end;
function TDataModel.GetFStringField1: string;
begin
Result := FStringField1;
end;
procedure TDataModel.SetFStringField1(Value: string);
begin
FStringField1 := Value;
end;
{ Identical getter/setter for StringField2 }
procedure TfrmMain.FillGUIFields;
var
i: integer;
TempBuffer: string;
begin
TempBuffer := '';
Randomize();
for i := 0 to Random(16) - 1 do begin
if Random(2) = 0 then
TempBuffer := TempBuffer + Chr(Random(25) + 65)
else
TempBuffer := TempBuffer + Chr(Random(25) + 97);
end;
StringField1.Text := TempBuffer; // Filling the edit field
{ Identical code for filling the memo field }
end;
end.
当我运行它时,在大约 27% 的情况下,我会遇到访问冲突异常。如果我只设置与 TEdit 字段名称匹配的属性(即 StringField1),则不会发生异常。如果我直接访问字段(让 getter/setter 直接指向字段或在 GUIToData 过程中使用 t.GetFields),则不会引发访问冲突。
人们能够重现这个吗?有谁知道是什么导致了这种奇怪的行为?谢谢!
【问题讨论】:
-
您使用的是哪个版本的 Delphi?
-
既然您说它在删除 setter 方法时有效,请尝试将“const”添加到 setter 中,如下所示:“SetFStringField1(const Value: string);”
-
@Ville:我正在运行 D2010。 const 建议真的很有趣,但是我无法验证它,因为我现在无法重现 AV。不过,我明天再试一次。
-
我在 Delphi XE 上运行它,每次都运行良好。显然,您的示例代码中缺少某些内容。 (或者您使用的是 D2010,这是 XE 中修复的错误。)
-
我刚刚在 D2010 中尝试过,每次都运行良好。
标签: delphi properties delphi-2010 access-violation rtti