class 的成员在分配时未更改
Member of a class isn't changed on assignment
我有以下设置:
//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);
...
出于我无法理解的原因,此行从未更改变量的值。我的代码的其他部分也存在类似的问题。 console 变量最初由控制台本身分配值 1。我想暂时将它设置得更高,以便在不重新编译控制台代码的情况下从应用程序获得更详细的输出(它在一个包中)。
这是因为您没有更改存储值,而是更改了它的副本。
Console.Variables['developer'].Value.SetValue(MESSAGELEVEL_VERBOSE);
这是试图进行修改的代码。 TStringEval
实例由 Value
属性:
生成
property Value: TStringEval read GetValue write SetValue;
您没有为 属性 显示 getter 但它必须 return 一个副本,因为 TStringEval
是一个记录,一个值类型。
解决该问题的一种方法是使 TStringEval
成为引用类型。也就是把它从一条记录转换成一条class。这是一个相当大的变化,您可能不愿意接受。
另一种选择是分配给 Value
而不是对其调用方法:
Console.Variables['developer'].Value := NewValue;
这导致了我认为是您代码中的基本设计缺陷。你有一个值类型,它的方法会改变 Self
。这是一个设计错误,许多不同的程序员已经犯了很多次。最近在 FireMonkey 库中发现了一些最引人注目的实例,这些实例反复出现此错误。
您问题中的问题说明了使用值类型方法改变值的原因。如果你的值类型没有变异方法,那么你就不会落入这个陷阱。因此,我建议您删除所有 SetValue
方法并将它们替换为具有 return 新值的静态 class 函数:
class function New(const S: string): TStringEval; static; overload;
那么修改值的唯一方法就是这样:
Console.Variables['developer'].Value := TStringEval.New(...);
事实上,您甚至可以使用隐式转换运算符来使语法更加简洁。
我有以下设置:
//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);
...
出于我无法理解的原因,此行从未更改变量的值。我的代码的其他部分也存在类似的问题。 console 变量最初由控制台本身分配值 1。我想暂时将它设置得更高,以便在不重新编译控制台代码的情况下从应用程序获得更详细的输出(它在一个包中)。
这是因为您没有更改存储值,而是更改了它的副本。
Console.Variables['developer'].Value.SetValue(MESSAGELEVEL_VERBOSE);
这是试图进行修改的代码。 TStringEval
实例由 Value
属性:
property Value: TStringEval read GetValue write SetValue;
您没有为 属性 显示 getter 但它必须 return 一个副本,因为 TStringEval
是一个记录,一个值类型。
解决该问题的一种方法是使 TStringEval
成为引用类型。也就是把它从一条记录转换成一条class。这是一个相当大的变化,您可能不愿意接受。
另一种选择是分配给 Value
而不是对其调用方法:
Console.Variables['developer'].Value := NewValue;
这导致了我认为是您代码中的基本设计缺陷。你有一个值类型,它的方法会改变 Self
。这是一个设计错误,许多不同的程序员已经犯了很多次。最近在 FireMonkey 库中发现了一些最引人注目的实例,这些实例反复出现此错误。
您问题中的问题说明了使用值类型方法改变值的原因。如果你的值类型没有变异方法,那么你就不会落入这个陷阱。因此,我建议您删除所有 SetValue
方法并将它们替换为具有 return 新值的静态 class 函数:
class function New(const S: string): TStringEval; static; overload;
那么修改值的唯一方法就是这样:
Console.Variables['developer'].Value := TStringEval.New(...);
事实上,您甚至可以使用隐式转换运算符来使语法更加简洁。