Добрый день.
Я столкнулся с интересной проблемой в большом проекте, где было много расчетов разных расстояний.
Несмотря на то, что данные собираются из разных источников, они могут быть в разных единицах измерения – здесь метры, там миллиметры.
Трудно за всем этим уследить, когда расчеты разбросаны повсюду.
А если переменная объявлена, то зачастую только автор подробно знает, в каких единицах она находится, поскольку комментариев в коде почти нет. А автор бросил/забыл/запил.
Это предлагает решение описывать каждую единицу как отдельный тип, например так:
Таким образом мы повысили читаемость кода.type TSizeMeter = single; TSizeMilliMeter = single;
Но как защититься от ошибок конвертации? В конце концов, эти типы совместимы, и их приведение не вызовет никаких ошибок или предупреждений (см.
здесь ).
Я пока не нашел способа отображать предупреждение при приведении типов, но мы можем написать автоматическое преобразование, если такое приведение происходит. Вот простейший пример со сложением и вычитанием: interface
type
TSizeMeter = record
value:single;
const units='m';
class operator Add(a, b: TSizeMeter): TSizeMeter;
class operator Subtract(a, b: TSizeMeter): TSizeMeter;
class operator Implicit(a: single): TSizeMeter;
class operator Implicit(a: TSizeMeter): single;
end;
TSizeMiliMeter = record
value:single;
const units='mm';
class operator Add(a, b: TSizeMiliMeter): TSizeMiliMeter;
class operator Subtract(a, b: TSizeMiliMeter): TSizeMiliMeter;
class operator Implicit(a: single): TSizeMiliMeter;
class operator Implicit(a: TSizeMiliMeter): single;
class operator Implicit(a: TSizeMiliMeter): TSizeMeter;
class operator Implicit(a: TSizeMeter): TSizeMiliMeter;
end;
implementation
class operator TSizeMeter.Add(a, b: TSizeMeter): TSizeMeter;
begin
result.value:=a.value+b.value;
end;
class operator TSizeMeter.Subtract(a, b: TSizeMeter): TSizeMeter;
begin
result.value:=a.value-b.value;
end;
class operator TSizeMeter.Implicit(a: single): TSizeMeter;
begin
result.value:=a;
end;
class operator TSizeMeter.Implicit(a: TSizeMeter): single;
begin
result:=a.value;
end;
class operator TSizeMiliMeter.Add(a, b: TSizeMiliMeter): TSizeMiliMeter;
begin
result.value:=a.value+b.value;
end;
class operator TSizeMiliMeter.Subtract(a, b: TSizeMiliMeter): TSizeMiliMeter;
begin
result.value:=a.value-b.value;
end;
class operator TSizeMiliMeter.Implicit(a: single): TSizeMiliMeter;
begin
result.value:=a;
end;
class operator TSizeMiliMeter.Implicit(a: TSizeMiliMeter): single;
begin
result:=a.value;
end;
class operator TSizeMiliMeter.Implicit(a: TSizeMiliMeter): TSizeMeter;
begin
result.value:=a.value/1000;
end;
class operator TSizeMiliMeter.Implicit(a: TSizeMeter): TSizeMiliMeter;
begin
result.value:=a.value*1000;
end;
И вот его использование: var v1:TSizeMeter;
v2:TSizeMiliMeter;
v3:TSizeMeter;
v4:TSizeMiliMeter;
begin
v1:=1.1;
v2:=111.1;
s1:=v1;
s2:=v2;
writeln(formatfloat('0.000',v1.value)+' '+v1.units+' or '+formatfloat('0.000',s1));
writeln(formatfloat('0.000',v2.value)+' '+v2.units+' or '+formatfloat('0.000',s2));
writeln('+');
v3:=v1+v2;
v4:=v1+v2;
writeln(formatfloat('0.000',v3.value)+' '+v3.units);
writeln(formatfloat('0.000',v4.value)+' '+v4.units);
writeln('-');
v3:=v1-v2;
v4:=v1-v2;
writeln(formatfloat('0.000',v3.value)+' '+v3.units);
writeln(formatfloat('0.000',v4.value)+' '+v4.units);
writeln('cast');
v3:=v2;
v4:=v1;
writeln(formatfloat('0.000',v3.value)+' '+v3.units);
writeln(formatfloat('0.000',v4.value)+' '+v4.units);
writeln('mix');
v3:=v2+22.22;
s1:=v1+33.33;
writeln(formatfloat('0.000',v3.value)+' '+v3.units);
writeln(formatfloat('0.000',s1));
end.
Что даст следующий результат:
1100 м или 1100 111 100 мм или 111 100 + 1211 м 1211 100 мм − 0,989 м 988 900 мм бросать 0,111 м 1100 000 мм смешивание 0,133 м 34,430Это решение не является идеальным, так как делает преобразование неочевидным, что может создать новые проблемы.
Но, если вы объявите все переменные правильного типа, проблем возникнуть не должно.
Более серьезное решение, которое ограничит приведение типов, — это вызвать исключение при попытке приведения, например: class operator TSizeMiliMeter.Implicit(a: TSizeMiliMeter): TSizeMeter;
begin
raise Exception.Create('Typecast not allowed');
end;
class operator TSizeMiliMeter.Implicit(a: TSizeMeter): TSizeMiliMeter;
begin
raise Exception.Create('Typecast not allowed');
end;
В этом случае мы получим ошибку в строке: v3:=v1+v2;
Вы можете развивать решение дальше, создав свой собственный тип выполнения.
Если кто сталкивался с подобными проблемами, поделитесь опытом в комментариях :) Наверняка есть более изящные решения, чем описанные выше.
P.S: Тест проводился в Delphi 10.1. Теги: #delphi #приведение типов #delphi
-
Некуда Записывать Российские Переговоры
19 Oct, 24 -
Youtube Create, Или Разговор С Роботом
19 Oct, 24 -
Хьюлетт, Паккард, Калифорния!
19 Oct, 24 -
Скрытие Облака Тегов
19 Oct, 24