Delphi 类的成员是';我的任务没有改变

Delphi 类的成员是';我的任务没有改变,delphi,Delphi,我有以下设置: //code from unit ... TObjectList<T:TObject>=class(TObject) private function GetItem(Name: string): T; function GetItemByIndex(Index: Integer): T; public property Items[Name:string]:T read GetItem;default; property Item[Index:Int

我有以下设置:

//code from unit
...
TObjectList<T:TObject>=class(TObject)
private
  function GetItem(Name: string): T;
  function GetItemByIndex(Index: Integer): T;
public
  property Items[Name:string]:T read GetItem;default;
  property Item[Index:Integer]:T read GetItemByIndex;
end;

...
{ TObjectList<T> }

function TObjectList<T>.GetItem(Name: string): T;
begin
Result:=T(FindComponent(Name));
end;

function TObjectList<T>.GetItemByIndex(Index: Integer): T;
begin
Result:=T(Components[Index]);
end;
...
TStringEval=record
private
  FValue:string;
public
  function AsString:string;
  function AsInteger:Integer;
  function AsFloat:Double;
  function AsBoolean:Boolean;
  function AsDateTime:TDateTime;
  function AsHex:string;
  procedure SetValue(const S:string);overload;
  procedure SetValue(const I:Integer;const AsHex:boolean=false);overload;
  procedure SetValue(const F:Double);overload;
  procedure SetValue(const B:Boolean);overload;
  procedure SetValue(const D:TDateTime);overload;
...
TConsoleVariable=class(TConsoleCommand)
...
  property Value:TStringEval read GetValue write SetValue;
...
TConsole=class(TObjectList<TConsoleCommand>)
...
  property Variables[Name:string]:TConsoleVariable read GetVariable;
...

function TConsole.GetVariable(Name: string): TConsoleVariable;
begin
Result:=TConsoleVariable(Items[Name]);
end;
...
//main application code, the troublesome line.
Console.Variables['developer'].Value.SetValue(MESSAGELEVEL_VERBOSE);
...
//来自单元的代码
...
TObjectList=类(TObject)
私有的
函数GetItem(名称:string):T;
函数GetItemByIndex(索引:整数):T;
公众的
属性项[名称:字符串]:T读取GetItem;违约
属性项[索引:整数]:T读取GetItemByIndex;
结束;
...
{TObjectList}
函数TObjectList.GetItem(名称:string):T;
开始
结果:=T(FindComponent(Name));
结束;
函数TObjectList.GetItemByIndex(索引:整数):T;
开始
结果:=T(成分[指标]);
结束;
...
TStringEval=记录
私有的
FValue:string;
公众的
函数AsString:string;
函数AsInteger:整数;
函数AsFloat:Double;
函数AsBoolean:Boolean;
函数AsDateTime:TDateTime;
函数AsHex:string;
过程设置值(常量S:字符串);超载;
过程SetValue(常数I:整数;常数AsHex:布尔=false);超载;
程序设定值(常数F:Double);超载;
过程设置值(常量B:布尔值);超载;
程序设定值(常数D:TDateTime);超载;
...
TConsoleVariable=类(TConsoleCommand)
...
属性值:TStringEval read GetValue write SetValue;
...
t解决方案=类(TObjectList)
...
属性变量[名称:字符串]:TConsoleVariable read GetVariable;
...
函数TConsole.GetVariable(名称:string):TConsoleVariable;
开始
结果:=tConsolveVariable(项[名称]);
结束;
...
//主应用代码,麻烦行。
Console.Variables['developer'].Value.SetValue(MESSAGELEVEL\u VERBOSE);
...

由于我无法理解的原因,这一行从不更改变量的值。我的代码的其他部分也存在类似的问题。控制台变量最初由控制台本身赋值为1。我想暂时将其设置得更高,以便在不重新编译console代码的情况下从应用程序获得更详细的输出(它在一个包中)。

这是因为您没有更改存储值,而是更改了它的一个副本

Console.Variables['developer'].Value.SetValue(MESSAGELEVEL_VERBOSE);
这是试图进行修改的代码。
TStringEval
实例由
Value
属性生成:

property Value: TStringEval read GetValue write SetValue;
您没有显示属性的getter,但它必须返回一个副本,因为
TStringEval
是一个记录,一个值类型

解决此问题的一种方法是将
TStringEval
设置为引用类型。也就是将其从记录转换为类。这是一个相当剧烈的变化,你可能不愿意接受

另一个选项是分配给
,而不是对其调用方法:

Console.Variables['developer'].Value := NewValue;
这导致了我认为代码中的基本设计缺陷。您有一个值类型,该值类型的方法会发生变异
Self
。这是一个设计错误,许多不同的程序员已经犯过很多次了。最近,在FireMonkey库中发现了一些最引人注目的实例,该库多次犯下此错误

您问题中的问题举例说明了为什么使用值类型的方法来改变值。如果您没有值类型的变异方法,那么您就不会落入这个陷阱。因此,我建议您删除所有
SetValue
方法,并用返回新值的静态类函数替换它们:

class function New(const S: string): TStringEval; static; overload;
那么修改该值的唯一方法如下所示:

Console.Variables['developer'].Value := TStringEval.New(...);

事实上,您甚至可以使用隐式cast操作符使语法更加空闲。

Eh,我甚至考虑过您的解决方案(创建一个返回新记录的静态方法),但放弃了它。我现在就试试。谢谢我怀疑你已经解释过了,但当时不确定。现在这一切都有了意义。很难检查无法编译的不完整代码;可能您的代码为隐藏的临时变量指定了一个值,而不是您需要的变量。作为记录,代码被放置在多个单元中,分布在应用程序和包中,您希望我如何显示它,以便您可以编译它?制作一个SSCE再现问题;这肯定不难。@ZzZombo我知道这样做需要时间,但最终结果是一个更好的问题。你还可以让你更容易地看到和探索问题,这总是好的。