有时我们想要一个可选参数
function doSomething(foo:Integer; bar:TObject=nil)
begin
if bar <> nil then // do something optional with bar
....
end
如何使用布尔值来执行等效操作,这允许我区分两个布尔值和“无值”?
function doSomething(foo:Integer; bar:Boolean=nil) // Won't compile
begin
if bar <> nil then // do something optional with bar
end
显然这不会编译为布尔值不能为零。
基本上,我想要一个具有三种可能状态的参数;真实,虚假或“未指明”。
答案 0 :(得分:18)
您可以使用重载执行此操作:
function doSomething(foo:Integer): Boolean; overload;
begin
// do something without bar
end;
function doSomething(foo:Integer; bar:Boolean): Boolean; overload
begin
// do something optional with bar
end;
然后您可以将其用作doSomething(1)
,以及doSomething(1, true)
使用您的示例,它将等同于:
function doSomething(foo:Integer; bar:Boolean=nil): Boolean; // Won't compile
begin
if bar <> nil then
// do something optional with bar
else
// do something without bar
end;
答案 1 :(得分:4)
布尔类型的值只能是True或False。您可以定义自己的类型,它有三种状态:True,False和Unspecified:
type ThreeStateBoolean = (True, False, Unspecified);
或者,您可以将指针传递给布尔值:
type PBoolean = ^Boolean;
function doSomething(bar: PBoolean = nil)
begin
if bar <> nil then
// do something with bar^
end
将指针传递给布尔值可能会很麻烦,具体取决于您如何调用它。
答案 2 :(得分:4)
另一个选项(如果你有一个相对现代版本的Delphi)是将它实现为一个记录,隐式转换为布尔值和从布尔值转换。通过运算符重载,您还可以启用三态逻辑。 如果您只需要偶尔使用它,那就太过分了,但是如果您确实需要三态逻辑系统,那么它的工作效果非常好,特别是因为您可以为它分配布尔值。 注意从3状态到2状态的分配。下面的示例将False分配给布尔值&lt; - 'Troolean'赋值,其中troolean是TNil,根据Delphi中的未分配布尔值,但有明显的复杂性。
请注意,这不是一个完整或有效的实施方式,它只是一个演示来指示可能的内容。顺便提一下,Jeroen Pluimers在可空类型上有一个很好的CodeRage视频。 This问题提供了一个链接。
unit UnitTroolean;
interface
type
TTroolean = record
private type
TThreeState = (TTrue = 1, TFalse = 0, TNil = -1);
var
fThreeState: TThreeState;
public
function AsString: string;
class operator Implicit(Value: boolean): TTroolean;
class operator Implicit(Value: TTroolean): boolean;
class operator Implicit(Value: TThreeState): TTroolean;
class operator Implicit(Value: TTroolean): TThreeState;
class operator LogicalAnd(Left, Right: TTroolean): TTroolean;
class operator LogicalOr(Left, Right: TTroolean): TTroolean;
class operator LogicalNot(Value: TTroolean): TTroolean;
end;
implementation
{ TRoolean }
class operator TTroolean.Implicit(Value: boolean): TTroolean;
begin
if Value then
result.fThreeState := TTrue
else
result.fThreeState := TFalse;
end;
class operator TTroolean.Implicit(Value: TTroolean): boolean;
begin
if not(Value.fThreeState = TNil) then
result := (Value.fThreeState = TTrue)
else
result := false;
end;
class operator TTroolean.Implicit(Value: TThreeState): TTroolean;
begin
result.fThreeState := Value;
end;
class operator TTroolean.Implicit(Value: TTroolean): TThreeState;
begin
result := Value.fThreeState;
end;
class operator TTroolean.LogicalAnd(Left, Right: TTroolean): TTroolean;
begin
if (Left.fThreeState = TNil) or (Right.fThreeState = TNil) then
result.fThreeState := TNil
else if ((Left.fThreeState = TTrue) and (Right.fThreeState = TTrue)) then
result.fThreeState := TTrue
else
result.fThreeState := TFalse;
end;
class operator TTroolean.LogicalNot(Value: TTroolean): TTroolean;
begin
begin
case value.fThreeState of
TNil: result.fThreeState:= TNil;
TTrue: result.fThreeState:= TFalse;
TFalse: result.fThreeState:= TTrue
end;
end;
end;
class operator TTroolean.LogicalOr(Left, Right: TTroolean): TTroolean;
begin
if (Left.fThreeState = TNil) or (Right.fThreeState = TNil) then
result.fThreeState := TNil
else if ((Left.fThreeState = TTrue) or (Right.fThreeState = TTrue)) then
result.fThreeState := TTrue
else
result.fThreeState := TFalse;
end;
function TTroolean.AsString: string;
begin
case ord(fThreeState) of
1:
result := 'TTrue';
0:
result := 'TFalse';
-1:
result := 'TNil';
end;
end;
end.
使用示例
program ThreeStateLogicTest;
{$APPTYPE CONSOLE}
uses
SysUtils,
UnitTroolean in 'UnitTroolean.pas';
var
ABoolean: boolean;
ATroolean, Anothertroolean, AThirdTroolean: TTroolean;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
write('Boolean:', BoolToStr(ABoolean, true), #10#13);
write(#10#13);
ATroolean := TFalse;
ABoolean := true;
ATroolean := ABoolean;
write('Boolean:', BoolToStr(ABoolean, true), #10#13);
write('Troolean:', ATroolean.AsString, #10#13);
write('Troolean as Boolean:', BoolToStr(ATroolean, true), #10#13);
write(#10#13);
ATroolean := TTrue;
ABoolean := false;
ATroolean := ABoolean;
write('Boolean:', BoolToStr(ABoolean, true), #10#13);
write('Troolean:', ATroolean.AsString, #10#13);
write('Troolean as Boolean:', BoolToStr(ATroolean, true), #10#13);
write(#10#13);
ABoolean := false;
ATroolean := TTrue;
ABoolean := ATroolean;
write('Boolean:', BoolToStr(ABoolean, true), #10#13);
write('Troolean:', ATroolean.AsString, #10#13);
write('Troolean as Boolean:', BoolToStr(ATroolean, true), #10#13);
write(#10#13);
ABoolean := true;
ATroolean := TFalse;
ABoolean := ATroolean;
write('Boolean:', BoolToStr(ABoolean, true), #10#13);
write('Troolean:', ATroolean.AsString, #10#13);
write('Troolean as Boolean:', BoolToStr(ATroolean, true), #10#13);
write(#10#13);
ABoolean := false;
ATroolean := Tnil;
ABoolean := ATroolean;
write('Boolean:', BoolToStr(ABoolean, true), #10#13);
write('Troolean:', ATroolean.AsString, #10#13);
write('Troolean as Boolean:', BoolToStr(ATroolean, true), #10#13);
write(#10#13);
ABoolean := true;
ATroolean := Tnil;
ABoolean := ATroolean;
write('Boolean:', BoolToStr(ABoolean, true), #10#13);
write('Troolean:', ATroolean.AsString, #10#13);
write('Troolean as Boolean:', BoolToStr(ATroolean, true), #10#13);
write(#10#13);
ATroolean := TTrue;
Anothertroolean := false;
AThirdTroolean := ATroolean and Anothertroolean;
write('And:', AThirdTroolean.AsString, #10#13);
AThirdTroolean := ATroolean or Anothertroolean;
write('Or:', AThirdTroolean.AsString, #10#13);
ATroolean := TNil;
Anothertroolean:= not ATroolean;
write('Not TNil:', Anothertroolean.AsString, #10#13);
ATroolean := TTrue;
Anothertroolean:= not ATroolean;
write('Not Ttrue:', Anothertroolean.AsString, #10#13);
ATroolean := Tfalse;
Anothertroolean:= not ATroolean;
write('Not Tfalse:', Anothertroolean.AsString, #10#13);
readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
答案 3 :(得分:0)
或者......您可以使用整数并根据需要将其强制转换为布尔值。使用0表示false,1表示true,-1表示“nil”。无论如何你切片它,你不能使用布尔变量做你想要的,你需要另一种类型或参数操作lynxnake建议。
编辑:对此的变化非常低效,将使用Variant。使用变量,您可以传入Null值(在某些方面类似于nil但仍然是值)以及Unassigned(也类似于nil,但代表“无值”)。
答案 4 :(得分:0)
最干净的方法是使用enumeration(a.k.a。enumerated type)。
这已在the Greg Hewgill's answer中显示 - 但错误的是,您不应使用预定义的false
和true
作为枚举标识符¹。并且在the HMcG's answer内 - 但在包装器类型(更复杂的示例)中。我建议写一些类似的内容:type TTrilean = (triFalse, triTrue, triNull);
。
或者,您可以使用StdCtrls
模块中的现有TCheckBoxState
类型,如果您不介意将VCL模块引入项目中。
此外,您可以按the Serhii Kheilyk's answer编写包装函数:
procedure DoSomething(Foo: Integer; Bar: TTrilean); overload;
begin
… //main code
end;
procedure DoSomething(Foo: Integer; Bar: Boolean); overload;
const
Trileans: array[Boolean] of TTrilean = (triFalse, triTrue);
begin
DoSomething(Foo, Trileans[Bar]);
end;
procedure DoSomething(Foo: Integer); overload;
begin
DoSomething(Foo, triNull);
end;
如果愿意,您甚至可以将第一个私有,最后两个公开。
<子>注:
1.我认为(不确定),正式你可以写type TMyType = (False, True, Unspecified);
,因为False
和True
不是保留字。但这会导致您无法访问False
类型的原始True
和Boolean
(之后您需要将其称为System.False
和System.True
)。这不是第三方编译器兼容的(例如FPC不允许这样做)。