2016-07-30 5 views
3

У меня есть класс со многими числовыми полями, количество полей будет расти с развитием проекта, поэтому было бы неплохо иметь способ сбросить все поля, независимо от того, сколько их будет добавлено в будущем.Delphi: Как я могу сбросить все поля классов до нулевого значения?

TParams = class 
    public 
    defined: boolean; 
    FirstValue:    byte; // reset from here 
    A0:  single; 
    A1:  single; 
    H1:  TPoint; 
     // ............... 
    A100:  single; 
    LastValue:    byte; // to here 
    procedure Reset; 
    end; 

Единственная идея приходит в голову, чтобы вставить 2 поля: до и после того, как блок, который должен быть сброшена, и использовать FillMemory:

procedure TParams.Reset; 
begin 
    FillMemory(@FirstValue, Integer(@LastValue)-Integer(@FirstValue),0); 
end; 

Есть ли лучший способ? (Im использованием Delphi 7)

+2

Поместите все поля в запись и обнулите запись. Не используйте уродливый взломать это. –

+1

Zeroizing не завершает работу над «управляемыми» участниками. Если есть только числовые поля, то обнуление будет прекрасным. –

ответ

6

Дэвид Heffernan уже сказал, что это: то, что вы делаете это некрасиво взломать и не очень чисто. Он также дал подсказку: Превратите свои числовые поля в записи:

type 
    TParams = class 
    public 
    Defined: Boolean; 
    Numbers: record 
     A0: Single; 
     A1: Single; 
     H1: TPoint; 
     A100: Single; 
    end; 
    procedure Reset; 
    end; 

Теперь это очень просто, вы можете Reset, выполнив следующие действия:

procedure TParams.Reset; 
begin 
    FillChar(Numbers, SizeOf(Numbers), 0); 
end; 

Простой тест:

procedure Test; 
var 
    P: TParams; 
begin 
    P := TParams.Create; 
    try 
    Writeln(Format('%f %f (%d, %d) %f', [P.Numbers.A0, P.Numbers.A1, P.Numbers.H1.X, P.Numbers.H1.Y, P.Numbers.A100])); 
    P.Numbers.A0 := 1.0; 
    P.Numbers.A1 := 2.0; 
    P.Numbers.H1 := Point(11, 22); 
    P.Numbers.A100 := 77.0; 
    Writeln(Format('%f %f (%d, %d) %f', [P.Numbers.A0, P.Numbers.A1, P.Numbers.H1.X, P.Numbers.H1.Y, P.Numbers.A100])); 
    P.Reset; 
    Writeln(Format('%f %f (%d, %d) %f', [P.Numbers.A0, P.Numbers.A1, P.Numbers.H1.X, P.Numbers.H1.Y, P.Numbers.A100])); 
    finally 
    P.Free; 
    end; 
end; 

в результате получается следующий вывод:

0.00 0.00 (0, 0) 0.00 
1.00 2.00 (11, 22) 77.00 
0.00 0.00 (0, 0) 0.00 

В качестве альтернативы, вы можете сделать следующее:

TNumbers = record 
    A0: Single; 
    A1: Single; 
    H1: TPoint; 
    A100: Single; 
    end; 

    TParams = class 
    public 
    Defined: Boolean; 
    Numbers: TNumbers; 
    procedure Reset; 
    end; 

Для тех, кто с версией с дженериков (я знаю, вы используете Delphi 7, которая не имеет дженерики, но в любом случае, для других), которые будут упростить Reset немного:

procedure TParams.Reset; 
begin 
    Numbers := Default(TNumbers); 
end; 

Default имеет то преимущество, что он будет должным образом завершить работу и инициализировать запись, только в случае, если это удалось типов (строки, интерфейсы и т.д.) в нем.

+0

Спасибо! Я знал, что фидельсы могут быть упакованы в рекорд, хотя есть способ не делать этого. Теперь я знаю, что другого пути нет :) –

+1

Я согласен, что не помещать их в запись было бы более удобно, потому что вы могли немедленно получите доступ к ним как 'P.A0' и т. д. вместо' P.Numbers.A0'. Но, кроме инициализации всех полей отдельно (нет большого числа, если их осталось всего несколько, хотя вы можете забыть его установить, если добавить поле), запись является единственным чистым решением. И как только вы сможете перейти на современную версию Delphi, используйте очиститель 'Default', вместо' ZeroMemory' или 'FillChar' или аналогичный. –

+0

FWIW, вместо использования маркеров * байтов *, таких как FirstValue и LastValue, вы могли бы использовать две пустые записи («FirstValue: конец записи»). ;-) Но, честно говоря, размещение нуберов в их собственной записи - это чистое решение. –

2

В некоторых ограниченных случаях, как ваш случай с TParams есть призыв к InitInstance должен сделать трюк:

procedure TParams.Reset; 
begin 
    InitInstance(Self); 
end; 
+2

Не называет ли 'InitInstance' его поле' defined' верным 'False'? OP не пытается сбросить все поля класса, но только некоторые из них. – SilverWarior

+2

@SilverWarior, да было бы. Я не знаю, если это необходимо. Я имею в виду только часть «иметь способ сбросить все поля» вопроса. BTW, он также потерпит неудачу, если есть поля управляемых типов. –