我有以下设置:
//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);
...
这行不会因为我无法理解的原因而改变变量的值。我的代码的其他部分也存在类似的问题。控制台变量最初由控制台本身分配值1。我想暂时将其设置得更高,以便从应用程序获得更详细的输出,而无需重新编译控制台代码(它在一个软件包中)。
答案 0 :(得分:5)
这是因为您没有更改存储的值,而是更改了它的副本。
Console.Variables['developer'].Value.SetValue(MESSAGELEVEL_VERBOSE);
这是尝试进行修改的代码。 TStringEval
实例由Value
属性生成:
property Value: TStringEval read GetValue write SetValue;
您没有显示该属性的getter,但它必须返回一个副本,因为TStringEval
是一个记录,一个值类型。
解决此问题的一种方法是将TStringEval
作为参考类型。那就是将它从一个记录转换为一个类。这是一个非常剧烈的变化,你可能不会理会。 p>
另一个选择是分配给Value
而不是调用方法:
Console.Variables['developer'].Value := NewValue;
这导致了我认为代码中的基本设计缺陷。您的值类型具有变异Self
的方法。这是许多不同程序员多次犯下的设计错误。最近,在FireMonkey库中发现了一些最引人注目的实例,它们反复提交此错误。
在您的问题中,问题是如何使用值类型的方法来改变值的原因。如果你没有改变你的值类型的方法,那么你就不会陷入这个陷阱。所以,我建议您删除所有SetValue
方法,并用返回新值的静态类函数替换它们:
class function New(const S: string): TStringEval; static; overload;
然后修改值的唯一方法是这样:
Console.Variables['developer'].Value := TStringEval.New(...);
实际上,您甚至可以使用隐式强制转换运算符来使语法更加省事。