代码之家  ›  专栏  ›  技术社区  ›  Thomas Koschel

布尔值作为方法参数是否不可接受?[闭门]

  •  122
  • Thomas Koschel  · 技术社区  · 17 年前

    我的一位同事说

    什么更容易理解?

    file.writeData( data, true );
    

    enum WriteMode {
      Append,
      Overwrite
    };
    
    file.writeData( data, Append );
    

    现在我明白了!;-)
    这无疑是一个示例,其中枚举作为第二个参数使代码更具可读性。

    26 回复  |  直到 17 年前
        1
  •  130
  •   skaffman    17 年前

    布尔值表示“是/否”选择。如果要表示“是/否”,则使用布尔值,它应该是自解释的。

        2
  •  50
  •   Mark Biek    17 年前

    枚举还允许将来进行修改,您现在需要第三种选择(或更多)。

        3
  •  32
  •   Jeremy Bourque    17 年前

    使用最能模拟您的问题的模型。在您给出的示例中,枚举是更好的选择。但是,布尔值在其他情况下会更好。这对你来说更有意义:

    lock.setIsLocked(True);
    

    enum LockState { Locked, Unlocked };
    lock.setLockState(Locked);
    

    在这种情况下,我可能会选择boolean选项,因为我认为它非常明确,而且我非常确定我的锁不会有两个以上的状态。尽管如此,第二种选择是有效的,但不必要的复杂,IMHO。

        4
  •  15
  •   Pascal Thivent    16 年前

    对我来说,使用布尔或枚举都不是一个好方法。罗伯特·C·马丁在他的著作中非常清楚地抓住了这一点 Clean Code Tip #12: Eliminate Boolean Arguments

    布尔参数大声声明函数做了不止一件事。它们令人困惑,应该予以消除。

    如果一个方法做了不止一件事,您应该编写两个不同的方法,例如在您的案例中: file.append(data) file.overwrite(data)

        5
  •  13
  •   Tim Jarvis    17 年前

    我想你几乎自己回答了这个问题,我认为最终目标是让代码更可读,在这种情况下,枚举做到了这一点,我认为最好看最终目标,而不是笼统的规则,也许把它更多地看作是一个指南,即枚举在代码中通常比一般布尔更可读,ints等,但该规则始终存在例外情况。

        6
  •  13
  •   Thorsten79    17 年前

    还记得阿德莱·史蒂文森在会议期间在联合国向佐林大使提出的问题吗 cuban missile crisis

    “你在世界法庭上 现在就发表意见,你可以回答 是还是不是 存在,我想知道我是否 我正确地理解了你。。。。我是 准备等待我的回答直到 决定。”

    如果方法中的标志的性质使您可以将其固定到 ,而该决定将 变成三向或n向决策,选择布尔值。指示:您的旗帜被称为 .

    模式开关 . 总是有 再来一个模式 比你一开始写这个方法时想到的要多。

    还有一个模式难题,例如,萦绕在Unix上,文件或目录可能具有的权限模式导致了奇怪的模式双重含义,这取决于文件类型、所有权等。

        7
  •  13
  •   John Kugelman Michael Hodel    11 年前

    有两个原因让我觉得这是件坏事:

    1. 因为有些人会编写如下方法:

      ProcessBatch(true, false, false, true, false, false, true);
      

    2. 因为通过简单的yes/no分支控制程序流可能意味着您有两个完全不同的函数,它们以一种awkard的方式封装成一个。例如:

      public void Write(bool toOptical);
      

      public void WriteOptical();
      public void WriteMagnetic();
      

      因为其中的代码可能完全不同;他们可能需要执行各种不同的错误处理和验证,甚至可能需要以不同的方式格式化输出数据。你不能仅仅通过使用 Write() 甚至 Write(Enum.Optical) (当然,您也可以使用这两种方法中的任何一种,如果需要,只需调用内部方法writeoptic/Mag即可)。

        8
  •  7
  •   Borek Bernard    17 年前

    枚举更好,但我不会将布尔参数称为“不可接受”。有时,只需输入一个布尔值,然后继续前进(想想私有方法等)

        9
  •  6
  •   Chris Lundie    17 年前

    Boolean在有命名参数的语言中可能是可以的,比如Python和Objective-C,因为名称可以解释参数的作用:

    file.writeData(data, overwrite=true)
    

    [file writeData:data overwrite:YES]
    
        10
  •  4
  •   csmba    17 年前

    我不同意这是个好主意 规则 . 显然,在某些情况下,Enum可以提供更好的显式或详细的代码,但一般来说,它似乎过于简单。

    首先让我以你为例: 程序员编写好代码的责任(和能力)并没有因为有一个布尔参数而受到损害。在您的示例中,程序员可以通过以下方式编写同样冗长的代码:

    dim append as boolean = true
    file.writeData( data, append );
    

    或者我更喜欢一般的

    dim shouldAppend as boolean = true
    file.writeData( data, shouldAppend );
    

    第二: 您给出的枚举示例只是“更好”,因为您正在传递一个常量。在大多数应用程序中,传递给函数的至少一些时间参数(如果不是大多数的话)是变量。在这种情况下,我的第二个示例(给变量起好名字)要好得多,而Enum给您带来的好处很小。

        11
  •  4
  •   Orion Edwards    17 年前

    实际上意味着

    属性(特别是使用C#3对象初始值设定项)或关键字参数(la ruby或python)是一种更好的方法,可以在其他情况下使用布尔参数。

    C#示例:

    var worker = new BackgroundWorker { WorkerReportsProgress = true };
    

    validates_presence_of :name, :allow_nil => true
    

    connect_to_database( persistent=true )
    

        12
  •  4
  •   cherouvim    16 年前

    的确,在许多情况下,枚举比布尔更具可读性和可扩展性,但“布尔不可接受”的绝对规则是愚蠢的。它是不灵活和适得其反的——它没有给人类的判断留下余地。它们是大多数语言中的一种基本内置类型,因为它们是有用的-考虑将其应用到其他内置类型:例如,“永远不使用int作为参数”将是疯狂的。

    这条规则只是风格的问题,而不是bug或运行时性能的潜在问题。更好的规则是“出于可读性的原因,更喜欢枚举而不是布尔”。

    看看.Net框架。布尔函数在很多方法中被用作参数。Net API并不完美,但我认为使用布尔值作为参数并不是一个大问题。工具提示总是给您参数的名称,您也可以构建这种指导-填写方法参数的XML注释,它们将出现在工具提示中。

    我还应该补充一点,当您的类或方法参数中有两个或多个布尔值,并且并非所有状态都有效(例如,将它们都设置为true是无效的)时,您应该将布尔值清晰地重构为枚举。

    例如,如果您的类具有如下属性

    public bool IsFoo
    public bool IsBar
    

    如果两者同时为真,这是一个错误,你实际上得到的是三个有效状态,更好地表示为:

    enum FooBarType { IsFoo, IsBar, IsNeither };
    
        13
  •  4
  •   Stephen C    14 年前

    您的同事最好遵守以下规则:

    • 选择最适合代码用户的内容。
    • 不要仅仅因为你喜欢这个月的形状就把星形的钉子砸进每个洞里!
        14
  •  3
  •   David Basarab    17 年前

    只有当您不打算扩展框架的功能时,才可以接受布尔值。首选枚举,因为您可以扩展枚举,而不会中断函数调用的以前实现。

        15
  •  2
  •   Jesse C. Slicer    17 年前

    如果该方法提出以下问题:

    KeepWritingData (DataAvailable());
    

    哪里

    bool DataAvailable()
    {
        return true; //data is ALWAYS available!
    }
    
    void KeepWritingData (bool keepGoing)
    {
       if (keepGoing)
       {
           ...
       }
    }
    

    布尔方法参数似乎具有绝对完美的意义。

        16
  •  2
  •   Greg Beech    17 年前

    这取决于方法。如果这个方法做了一些非常明显是真/假的事情,那么它是好的,例如下面[虽然我不是说这是这个方法的最佳设计,但它只是一个用法显而易见的例子]。

    CommentService.SetApprovalStatus(commentId, false);
    

    但是,在大多数情况下,例如您提到的示例,最好使用枚举。在.NET框架本身中,有许多示例没有遵循此约定,但这是因为他们在周期的后期引入了此设计指南。

        17
  •  2
  •   Jennifer    17 年前

        18
  •  2
  •   Robert Paulson    17 年前

    枚举当然可以使代码更具可读性。还有一些事情需要注意(至少在.net中)

    如果您的枚举对代码是私有的(从未公开),那么您可以停止阅读此处。

    如果您的枚举是 以任何方式,外部代码和/或保存在程序之外,考虑显式编号。编译器会自动从0开始对枚举进行编号,但如果重新排列枚举而不给它们赋值,则可能会导致缺陷。

    我可以合法写作

    WriteMode illegalButWorks = (WriteMode)1000000;
    file.Write( data, illegalButWorks );
    

    为了解决这一问题,任何使用您无法确定的枚举(例如公共API)的代码都需要检查该枚举是否有效。你可以通过

    if (!Enum.IsDefined(typeof(WriteMode), userValue))
        throw new ArgumentException("userValue");
    

    唯一的警告 Enum.IsDefined

    public static bool CheckWriteModeEnumValue(WriteMode writeMode)
    {
      switch( writeMode )
      {
        case WriteMode.Append:
        case WriteMode.OverWrite:
          break;
        default:
          Debug.Assert(false, "The WriteMode '" + writeMode + "' is not valid.");
          return false;
      }
      return true;
    }
    

    版本控制问题是,旧代码可能只知道如何处理现有的2个枚举。如果添加第三个值,Enum.IsDefined将为true,但旧代码不一定能处理它。哎呀。

    [Flags] 枚举,其验证代码略有不同。

    我还要注意,为了便于携带,您应该使用call ToString() 在枚举上,并使用 Enum.Parse() ToString() Enum.Parse() 能应付 enum也是,所以没有理由不使用它们。请注意,这是另一个陷阱,因为现在您甚至无法在不破坏代码的情况下更改枚举的名称。

    所以,有时候当你问自己的时候,你需要权衡以上所有因素

        19
  •  1
  •   Jurassic_C    17 年前

    依我看,对于任何可能有两个以上选项的情况,枚举都是显而易见的选择。但在某些情况下,布尔值就是您所需要的全部。在这种情况下,我会说在bool可以工作的地方使用enum就是一个使用7个单词的例子,而4个单词就可以了。

        20
  •  0
  •   Dan Udey    17 年前

        21
  •  0
  •   Drew Noakes    14 年前

    有时,用重载对不同的行为建模更简单。从您的示例继续:

    file.appendData( data );  
    file.overwriteData( data );
    

    如果您有多个参数,每个参数都允许一组固定的选项,那么这种方法会降低性能。例如,打开文件的方法可能有几种排列方式:文件模式(打开/创建)、文件访问(读/写)、共享模式(无/读/写)。配置的总数等于各个选项的笛卡尔乘积。当然,在这种情况下,多重重载是不合适的。

    通常,布尔参数作为新的重载附加到参数列表中。NET中的一个示例是:

    Enum.Parse(str);  
    Enum.Parse(str, true); // ignore case
    

    如果您知道只有两种选择,那么布尔值就可以了。枚举是可扩展的,不会破坏旧代码,尽管旧库可能不支持新的枚举值,因此不能完全忽略版本控制。


    编辑

    Enum.Parse(str, ignoreCase: true);
    
        22
  •  0
  •   Haris Krajina    14 年前

    我同意枚举是一个很好的方法,在方法中有两个选项(只有两个选项在没有枚举的情况下具有可读性)

    例如

    public void writeData(Stream data, boolean is_overwrite)
    

        23
  •  0
  •   Robert Martin    13 年前

    这是一篇旧文章的最新条目,它在页面上的位置太低了,以至于没有人会读它,但是因为没有人已经说过了。。。。

    内联注释对于解决意外问题有很大帮助 bool 问题最初的例子尤其令人发指:想象一下试图在函数declearanation中命名变量!大概是

    void writeData( DataObject data, bool use_append_mode );
    

    但是,为了举例,让我们假设这是一个声明。然后,对于一个无法解释的布尔参数,我将变量名放在一个内联注释中。比较

    file.writeData( data, true );
    

    具有

    file.writeData( data, true /* use_append_mode */);
    
        24
  •  -1
  •   CheeZe5    17 年前

        25
  •  -1
  •   MusiGenesis    17 年前

    在您的示例中使用枚举而不是布尔值确实有助于使方法调用更具可读性。但是,这是我最喜欢的C#中的愿望项的替代品,即方法调用中的命名参数。此语法:

    var v = CallMethod(pData = data, pFileMode = WriteMode, pIsDirty = true);
    

    将是完全可读的,然后您可以做程序员应该做的事情,即为方法中的每个参数选择最合适的类型,而不考虑它在IDE中的外观。

    C#3.0允许在构造函数中使用命名参数。我不知道为什么他们也不能用方法做到这一点。

        26
  •  -1
  •   fastcodejava    16 年前

    布尔值 true / false 只有因此,不清楚它代表了什么。 Enum 可以有有意义的名称,例如 OVERWRITE APPEND

    推荐文章