You can use advanced records to accomplish this. For instance, you could do
type
TFileName = record
FFileName: string;
public
class function IsValidFileName(const S: string): boolean; static;
class operator Implicit(const S: string): TFileName;
class operator Implicit(const S: TFileName): string;
end;
implementation
class function TFileName.IsValidFileName(const S: string): boolean;
begin
result := true {TODO};
end;
class operator TFileName.Implicit(const S: string): TFileName;
begin
if IsValidFileName(S) then
result.FFileName := S
else
raise Exception.CreateFmt('Invalid file name: "%s"', [S]);
end;
class operator TFileName.Implicit(const S: TFileName): string;
begin
result := S.FFileName;
end;
and similarly for TPath
and TFolder
. Advantages:
- A function expecting
TPath
will not accept aTFileName
(or some other combination). - You can still assign a
TPath
to/from a regular string. If you cast from a string to aTPath
, you will automatically check the string to see if it contains a valid path. - Optionally, you can specify how a
TPath
can be assigned to aTFileName
(or some other combination), by writing moreImplicit
class operators.