2011-09-22 34 views
9

Đôi khi chúng ta muốn có một tham số tùy chọnVượt qua một tùy chọn biến boolean

function doSomething(foo:Integer; bar:TObject=nil) 
begin 
    if bar <> nil then // do something optional with bar 
    .... 
end 

Làm thế nào để làm tương đương với một boolean, cho phép tôi để phân biệt giữa hai giá trị boolean và "không có giá trị"?

function doSomething(foo:Integer; bar:Boolean=nil) // Won't compile 
begin 
    if bar <> nil then // do something optional with bar 
end 

Rõ ràng điều này sẽ không biên dịch dưới dạng Boolean không thể là không.

Về cơ bản, tôi muốn tham số có ba trạng thái có thể; đúng, sai hoặc "không xác định".

Trả lời

18

Bạn có thể làm cách nào khác này, sử dụng quá tải:

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; 

Sau đó, bạn có thể sử dụng nó như doSomething(1), cũng như doSomething(1, true)

Sử dụng ví dụ của bạn, nó sẽ tương đương với:

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 Đây là cách tốt nhất để đạt được điều này, IMO. Có ba trạng thái: 'doSomething (1)', 'doSomething (1, True)' và 'doSomething (1, False)'. –

+0

@Rudy, vâng, nhưng chỉ vì OP xác định cần phải làm việc với các tham số * Boolean *. Nếu không, tôi nghĩ rằng 3 đường dẫn mã riêng biệt được phân biệt hiệu quả hơn bằng cách sử dụng một biến trạng thái 3 (ví dụ như ví dụ ThreeStateBoolean của Greg Hewgill). – Sam

+0

@Rudy, điều này làm cho câu trả lời tốt hơn là nó tuân theo câu của OP "đôi khi chúng ta muốn tham số tùy chọn", không phải là ba trạng thái được hỗ trợ (vì cả hai câu trả lời đều làm như vậy). IMO. – Sam

4

Giá trị của loại Boolean chỉ có thể là True hoặc False. Bạn có thể xác định loại của riêng bạn mà có ba trạng thái: True, False, và Unspecified:

type ThreeStateBoolean = (True, False, Unspecified); 

Hoặc, bạn có thể vượt qua một con trỏ đến một Boolean:

type PBoolean = ^Boolean; 

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

Đi qua một con trỏ đến một Boolean có thể khó xử tùy thuộc vào cách bạn đang gọi nó.

+0

chỉ phần đầu của câu trả lời là chính xác, bạn không thể khai báo một hàm như thế này trong delphi, bạn phải sử dụng các kiểu được xác định trước trong các tham số như 'PBoolean', vì vậy phải giống như 'procedure doSomething2 (thanh: PBoolean = nil); ' – RRUZ

+0

Cảm ơn, tôi sẽ thay đổi điều đó. Đã được một thời gian rất dài kể từ khi tôi được mã hóa trong Pascal ... –

+3

loại ThreeStateBoolean = (True, False, Unspecified); Tuy nhiên, lưu ý rằng bạn không thể thực sự sử dụng True và False mà không ghi đè lên các khai báo boolean True và False. Điều này sẽ cung cấp cho các lỗi biên dịch bất cứ nơi nào bạn thực sự sử dụng booleans. TTrue, TFalse, Unspecified sẽ hoạt động. – HMcG

0

Hoặc ... bạn có thể sử dụng một số nguyên và chuyển nó vào Boolean nếu cần. Sử dụng 0 cho false, 1 cho true và -1 cho "nil". Dù sao bạn cắt nó, bạn không thể sử dụng một biến Boolean để làm những gì bạn muốn, bạn sẽ cần một kiểu khác hoặc một thao tác các tham số như lynxnake đề xuất.

EDIT: Một biến thể về điều này, rất không hiệu quả, sẽ là sử dụng Biến thể. Với một biến thể bạn có thể chuyển vào các giá trị Null (tương tự như nil theo một số cách nhưng vẫn là một giá trị) cũng như Unassigned (cũng tương tự như nil, nhưng đại diện cho "không có giá trị").

4

Một tùy chọn khác (nếu bạn có phiên bản tương đối hiện đại cho Delphi) là triển khai bản ghi này dưới dạng bản ghi, với chuyển đổi ngầm đến và từ giá trị boolean. Với quá tải toán tử, bạn cũng có thể kích hoạt logic 3 trạng thái. Đây là quá mức cần thiết nếu tất cả những gì bạn yêu cầu thỉnh thoảng sử dụng, nhưng nếu bạn cần hệ thống logic ba trạng thái, nó hoạt động rất độc đáo, đặc biệt khi bạn có thể gán các giá trị boolean cho nó. Hãy cẩn thận với các bài tập từ suy nghĩ từ 3 tiểu bang đến 2 tiểu bang. Ví dụ dưới đây gán False cho Boolean < - sự phân công 'Troolean' nơi troolean là TNil, theo một boolean chưa được gán trong Delphi, nhưng có những biến chứng rõ ràng.

Xin lưu ý rằng đây không phải là cách triển khai hoàn chỉnh hoặc hiệu quả bằng bất kỳ phương tiện nào, đó chỉ là bản trình diễn để xác định những gì có thể. Ngẫu nhiên, có một CodeRage vidoe tốt bởi Jeroen Pluimers trên các kiểu nullable. Câu hỏi This cung cấp liên kết.

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. 

Và một ví dụ về sử dụng

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

Tôi thậm chí không biết Delphi có quá tải toán tử. – awmross

0

Cách sạch là sử dụng enumeration (còn gọi là enumerated type).

Điều này đã được hiển thị trong the Greg Hewgill's answer - nhưng không chính xác, bạn không nên sử dụng các mã định danh được xác định trước falsetrue làm định danh liệt kê¹. Và trong phạm vi the HMcG's answer - nhưng trong loại trình bao bọc (ví dụ phức tạp hơn). Tôi đề nghị viết một cái gì đó như: type TTrilean = (triFalse, triTrue, triNull);.

Hoặc, bạn có thể sử dụng loại TCheckBoxState hiện tại từ mô-đun StdCtrls, nếu bạn không nhớ đưa mô-đun VCL vào dự án của mình.


Ngoài ra bạn có thể viết các hàm wrapper mỗi 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; 

Bạn thậm chí có thể làm cho một tư nhân đầu tiên và cuối cùng hai công cộng, nếu bạn muốn.


Ghi chú:
1. Tôi nghĩ (không chắc chắn), chính thức bạn có thể viết type TMyType = (False, True, Unspecified);, như FalseTrue là những từ không dành riêng. Nhưng điều này sẽ phá vỡ quyền truy cập của bạn vào số FalseTrue của loại Boolean (bạn sẽ cần phải giới thiệu chúng dưới dạng và System.True sau đó). Và đó không phải là tương thích với trình biên dịch của bên thứ ba (ví dụ: FPC sẽ không cho phép điều đó).

Các vấn đề liên quan