传递一个可选的布尔变量

时间:2011-09-22 01:47:47

标签: delphi

有时我们想要一个可选参数

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

显然这不会编译为布尔值不能为零。

基本上,我想要一个具有三种可能状态的参数;真实,虚假或“未指明”。

5 个答案:

答案 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中显示 - 但错误的是,您不应使用预定义的falsetrue作为枚举标识符¹。并且在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);,因为FalseTrue不是保留字。但这会导致您无法访问False类型的原始TrueBoolean(之后您需要将其称为System.FalseSystem.True)。这不是第三方编译器兼容的(例如FPC不允许这样做)。