2011-09-22 16 views
9

を渡し時々、私は私が2つのブール値と「無価値」を区別することを可能にするブール値と同等のものを、どのように行うのですはオプションのブール変数

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 

明らかに、これはブール値をゼロにすることはできないため、コンパイルされません。

基本的に、3つの可能な状態のパラメータが必要です。 true、false、または "unspecified"です。

答えて

18

あなたは、オーバーロードを使用して、この他の方法を行うことができます。

:次に、あなたの例を使用して doSomething(1)だけでなく、 doSomething(1, true)

として使用することができます

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; 

、それは同じになります

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; 
+0

+1これを達成するための最良の方法は、IMOです。 'doSomething(1)'、 'doSomething(1、True)'、 'doSomething(1、False)'の3つの状態があります。 –

+0

@Rudy、はい、しかしOPが*ブール*パラメータで作業する必要があることを指定したからです。さもなければ、3つの異なるコードパスが、単一の3つの状態変数(Greg HewgillのThreeStateBooleanの例など)を使用することによって、より効率的に区別されると思います。 – Sam

+0

@Rudy、これはより良い答えになるのは、OPの文に「時にはオプションのパラメータが必要です」ということです.3つの状態がサポートされているわけではありません。 IMO。 – Sam

4

ブール型の値はTrueまたはFalseのみです。 TRUE、FALSE、および未指定:

type ThreeStateBoolean = (True, False, Unspecified); 

それとも、あなたはブールにポインタを渡すことができます。

へのポインタを渡す
type PBoolean = ^Boolean; 

function doSomething(bar: PBoolean = nil) 
begin 
    if bar <> nil then 
     // do something with bar^ 
end 

次の3つの状態があり、独自の型を定義することができますあなたがそれをどのように呼び出しているかによって、ブール値は扱いにくいかもしれません。

+0

答えの最初の部分は正しいですが、delphiではこのような関数を宣言することはできません。事前定義された型を 'PBoolean'のようなパラメータで使用する必要があります。したがって、' procedure doSomething2(bar:PBoolean = nil); ' – RRUZ

+0

ありがとう、私はそれを変更します。私がパスカルでコード化して以来、非常に長い時間でした。 –

+3

タイプThreeStateBoolean =(True、False、Unspecified); しかし、実際にTrueとFalseを使用することはできません。システムのboolean TrueとFalse宣言を上書きしないでください。これにより、ブーリアンを実際に使用するたびにコンパイルエラーが発生します。 TTrue、TFalse、Unspecifiedが動作します。 – HMcG

0

またはIntegerを使用して、必要に応じてBooleanにキャストすることができます。 falseの場合は0、trueの場合は1、 "nil"の場合は-1を使用します。とにかくそれをスライスし、ブール変数を使用してあなたが望むことをすることはできません.Lynxnakeが提案するように、別のタイプやパラメータの操作が必要になります。

EDIT:非常に非効率なこのバリエーションは、バリアントを使用することです。バリアントを使用すると、Null値(何らかの理由でnilに似ていますが、それでも値)とUnassigned(nilに似ていますが、値がないことを表します)を渡すことができます。

4

もう1つのオプション(Delphiの最新のバージョンを使用している場合)は、これをレコードとして実装し、ブール値との間の暗黙的な変換を行います。演算子のオーバーロードによって、3ステートロジックを有効にすることもできます。 あなたが必要とするものが時々使用されるのなら、これは過剰ですが、3状態のロジックシステムが必要な場合は、特にブール値を割り当てることができるので、非常にうまく動作します。 スリーステートから2-ステートへの割り当てに注意してください。以下の例では、FALSEをブール値<に割り当てています.Trooleanは、Delphiの割り当てられていないブール値のようにTNilであり、明らかな複雑さがあります。

これは完全にまたは効率的な実装ではありませんが、可能なことを告げるデモです。ちなみに、null可能な型のJeroen PluimersによるよいCodeRage vidoeがあります。 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. 
+0

私はDelphiに演算子のオーバーロードがあったことさえ知りませんでした。 – awmross

0

例クリーンな方法は、enumeration(別名enumerated type)を使用することです。

これはすでにthe Greg Hewgill's answerに表示されていますが、間違って、列挙識別子¹としてあらかじめ定義されたfalsetrueを使用しないでください。 the HMcG's answer内ですが、ラッパータイプ(複雑な例)内です。私はtype TTrilean = (triFalse, triTrue, triNull);のようなものを書くことをお勧めします。

また、VCLモジュールをプロジェクトに持っていけば、モジュールの既存のTCheckBoxStateタイプを使用することができます。


また、あなたは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; 

ご希望の場合でも、最初のものは、民間との最後の2つのパブリック作ることができます。


注:
1.私はFalseTrueは予約語ではないとして(わからない)、正式にあなたは、type TMyType = (False, True, Unspecified);を書くことができると思います。しかし、BooleanのオリジナルのFalseTrueへのアクセスは解除されます(それ以降はとSystem.Trueとする必要があります)。これは第三者コンパイラ互換ではありません(FPCなどでは許可されません)。