Primitieve types hebben standaard conversieregels; veel programmeertalen werken standaard zo. Dit zorgt ervoor dat je niet alles hoeft te casten en is puur voor het gemak. Je kunt bijvoorbeeld een Integer probleemloos aan een Extended toewijzen, zowel door een letterlijke 42 mee te geven als door een variabele van het type Integer toe te wijzen.
procedure Hello;
var
SomeNumber: Extended;
AnotherNumber: Integer;
begin
AnotherNumber := 6;
SomeNumber := 2; // works
SomeNumber := AnotherNumber; // works
end;
Zelfs als je type-aliasing toepast, blijven de types dezelfde types als voorheen en gelden er automatische conversieregels:
type
ANewType = Extended;
AnotherNewType = Integer;
implementation
procedure Hello;
var
SomeNumber: ANewType;
AnotherNumber: AnotherNewType;
begin
AnotherNumber := 6;
SomeNumber := 2; // works
SomeNumber := AnotherNumber; // works
end;
Wil je echt strikte conversieregels, bijvoorbeeld omdat je wilt voorkomen dat meters aan seconden worden toegekend of andersom, dan moet je nieuwe types aanmaken. En nieuwe types maak je met behulp van een record of een class.
Om bepaalde conversies toch toe te staan, kun je die regels aan de types toevoegen met operator overloading (zie Embarcadero Wiki).
In FreePascal is de syntaxis anders, maar je kunt hetzelfde bereiken (zie FreePascal Reference).
Je kunt vervolgens zelfs conversieregels maken voor meters * seconden, waarmee je een meter-per-seconde-type creëert.
Hier is een voorbeeld van hoe dat werkt in Delphi:
type
TMetersPerSecond = record
private
m: Extended;
s: Extended;
value: extended;
mset: Boolean;
sset: Boolean;
function Units: string;
public
function ToString: string;
end;
TMeters = record
private
value: Extended;
function Units: string;
public
class operator Implicit(Value: Extended): TMeters ;
class operator Explicit(Value: TMeters ): TMetersPerSecond ;
class operator Multiply(ms: TMetersPerSecond; m: TMeters ): TMetersPerSecond;
function ToString: string;
end;
TSeconds = record
private
value: Extended;
function Units: string;
public
class operator Implicit(Value: Extended): TSeconds;
class operator Explicit(Value: TSeconds): TMetersPerSecond;
class operator Multiply(ms: TMetersPerSecond; s: TSeconds): TMetersPerSecond;
function ToString: string;
end;
implementation
uses
SysUtils;
class operator TMeters.Implicit(Value: Extended): TMeters;
begin
Result.value := Value;
end;
class operator TSeconds.Implicit(Value: Extended): TSeconds;
begin
Result.value := Value;
end;
class operator TMeters.Explicit(Value: TMeters): TMetersPerSecond;
begin
Result.m := Value.value;
Result.mset := True;
if Result.sset then
Result.value := Result.s * Result.m;
end;
class operator TSeconds.Explicit(Value: TSeconds): TMetersPerSecond;
begin
Result.s := Value.value;
Result.sset := True;
if Result.mset then
Result.value := Result.s * Result.m;
end;
class operator TSeconds.Multiply(ms: TMetersPerSecond; s: TSeconds): TMetersPerSecond;
begin
Result := ms;
Result.s := s.value;
Result.sset := True;
if Result.mset then
Result.value := Result.s * Result.m;
end;
class operator TMeters.Multiply(ms: TMetersPerSecond; m: meters): TMetersPerSecond;
begin
Result := ms;
Result.m := m.value;
Result.mset := True;
if Result.sset then
Result.value := Result.s * Result.m;
end;
function TMetersPerSecond.Units: string;
begin
Result := 'm/s';
end;
function TMetersPerSecond.ToString: string;
begin
Assert(mset and sset);
Result := Format('%f%s', [value, Units])
end;
function TMeters.Units: string;
begin
Result := 'm';
end;
function TMeters.ToString: string;
begin
Result := Format('%f%s', [value, Units]);
end;
function TSeconds.Units: string;
begin
Result := 's';
end;
function TSeconds.ToString: string;
begin
Result := Format('%f%s', [value, Units]);
end;
var
time: TSeconds;
distance: TMeters;
speed: TMetersPerSecond;
different_speed: TMetersPerSecond;
initialization
time := 10.0;
distance := 3;
// distance := time; // does not work, because seconds are not meters!
speed := TMetersPerSecond(time) * distance;
// or the other way around
speed := TMetersPerSecond(distance) * time;
WriteLn('speed -> ' + time.ToString + ' * ' + distance.ToString + ' = ' + Speed.ToString);
// different_speed := distance; // this doesn't work without explicit conversion
WriteLn('different_speed -> ' + different_speed.ToString);
end.
Contact