V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
banli
V2EX  ›  问与答

编程过程中, 你是习惯提前返回, 还是统一返回。

  •  
  •   banli · 2017-04-24 10:42:50 +08:00 · 6926 次点击
    这是一个创建于 2768 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在编程的过程中, 每个人都有每个人的编程习惯。 就比如在写一个函数的时候, 我就喜欢判断一下不符合预期的接口, 提前报错或者提前返回,这样写下了代码会容易看懂, 没有复杂的嵌套判断。 。

    然而还一种习惯,就是习惯统一返回。这样就只有一个地方返回,比较好测试预估。但是相应的,代码的各种判断嵌套可能就比提前返回来的多了,看逻辑会吃力一些。

    你觉得呢?

    75 条回复    2017-04-25 14:19:41 +08:00
    jedrek
        1
    jedrek  
       2017-04-24 11:08:20 +08:00
    我觉得这两种并没有好坏之分。我两种都用,主要是看函数里的逻辑情况,捡逻辑最清晰、最易读的方式写
    lany
        2
    lany  
       2017-04-24 11:10:17 +08:00
    哪种不费脑用哪种,脑子能不用最好。。。
    murmur
        3
    murmur  
       2017-04-24 11:10:41 +08:00
    有的时候提前返回可以少写 if/else ,尤其是因为参数不合法的
    sagaxu
        5
    sagaxu  
       2017-04-24 11:13:19 +08:00
    统一返回,是 bad smell
    815lbh
        6
    815lbh  
       2017-04-24 11:18:20 +08:00
    提前返回
    rozbo
        7
    rozbo  
       2017-04-24 11:18:56 +08:00   ❤️ 1
    看起来你们都不写 c 和 c++等手动内存管理的语言啊。
    在这种语言中,不能够提前返回,否则可能上面申请的内存忘记释放,或者句柄忘记关闭造成内存泄露或僵尸对象。
    当然,即使是自动内存管理的语言,也存在需要手动管理的资源。这个时候提前返回同样可能导致问题的发生。
    因此,我建议尽量都使用一个返回语句。除非你确定提前返回不会导致问题的发生。而且,在多人协同工作的时候,不会因为别人在前面加了代码而导致问题的发生。
    在我们团队,要求所有人在每个函数或者方法里都有且只能有一个 return 语句。
    huijiewei
        8
    huijiewei  
       2017-04-24 11:19:48 +08:00
    提前返回
    coderluan
        9
    coderluan  
       2017-04-24 11:33:26 +08:00   ❤️ 2
    @rozbo

    手动管理内存方法很多,可以创建内存池统一管理,可以规定只在构造之后申请内存,或者是提前返回需要特定错误值,你这只是一种解决方法而已,又不是唯一的方法,怎么能看出来别人不写 C++呢,我反而是感觉你 C++写的少。
    sagaxu
        10
    sagaxu  
       2017-04-24 11:36:50 +08:00   ❤️ 4
    @rozbo 写 C++的会不知道 RAII?
    Shura
        11
    Shura  
       2017-04-24 11:39:09 +08:00 via Android
    @rozbo 这个语言并没有任何关系,习惯而已。
    rozbo
        12
    rozbo  
       2017-04-24 11:42:09 +08:00
    @coderluan 你说的这几种方案对于你个人来说是可以的,但对于团队成员来说就不是那么容易接受的了,在你提出的这几种方案中,实现成本最小的无疑是约束团队成员用统一的返回方式。
    你能保证你团队中没有新手吗?
    coderluan
        13
    coderluan  
       2017-04-24 11:53:07 +08:00
    @rozbo

    我们团队确实没啥新手,假设有的话,也是应该他自己去学习,而不是我们找个简单的办法迁就他。另外实现成本最小的就是 RAII ,简单来说构造申请析构释放,再新手也能学会吧。
    jarell
        14
    jarell  
       2017-04-24 11:58:00 +08:00   ❤️ 1
    @rozbo goto
    rozbo
        15
    rozbo  
       2017-04-24 11:58:29 +08:00
    @coderluan
    char * a=(char*) malloc(0x1000);
    这种的,怎么 RAII ?
    rozbo
        16
    rozbo  
       2017-04-24 11:58:44 +08:00
    @jarell 这个屌。
    jarell
        17
    jarell  
       2017-04-24 12:01:11 +08:00
    @rozbo 该 goto 时就 goto 自己写的开心就好
    mkdong
        18
    mkdong  
       2017-04-24 12:01:13 +08:00 via iPhone
    同意 goto ,不仅可以减少不必要的嵌套,还可以统一返回
    0915240
        19
    0915240  
       2017-04-24 12:03:59 +08:00 via iPhone
    fast failed
    dingz
        20
    dingz  
       2017-04-24 12:13:19 +08:00
    @rozbo auto_ptr
    liul85
        21
    liul85  
       2017-04-24 12:23:39 +08:00
    提前返回 少写 if , else
    gamexg
        22
    gamexg  
       2017-04-24 12:24:25 +08:00 via Android   ❤️ 1
    @rozbo c 这么说还算合适,但是 c++忘记释放内存? 智能指针想哭。
    chunqiuyiyu
        23
    chunqiuyiyu  
       2017-04-24 12:27:23 +08:00 via iPhone
    能提前返回就一定要提前返回
    coderluan
        24
    coderluan  
       2017-04-24 12:46:58 +08:00
    @rozbo

    用 a 当类成员变量,该咋整咋整呗,要不你告诉我为啥你认为这种不能 RAII 吧?
    otakustay
        25
    otakustay  
       2017-04-24 13:01:20 +08:00
    使用符合语义的提前返回
    lrxiao
        26
    lrxiao  
       2017-04-24 13:09:19 +08:00
    @dingz 17 年了还 auto_ptr... 难道被三体人锁死了编译器吗
    lrxiao
        27
    lrxiao  
       2017-04-24 13:10:41 +08:00   ❤️ 1
    @rozbo std::unique_ptr<char[]> p{new char[1000]};
    dingz
        28
    dingz  
       2017-04-24 13:20:17 +08:00
    @lrxiao 不好意思,表述不当,是想说“智能指针”的,没有特指啥哈
    think2011
        29
    think2011  
       2017-04-24 13:21:35 +08:00
    提前返回,容易看到
    wuethan
        30
    wuethan  
       2017-04-24 13:30:44 +08:00
    封装函数更适合提前返回··· 我喜欢清晰的感觉 无限嵌套思路混乱··· 另外只做过串口和数据库交互 没遇到什么需要关闭的问题,数据库都是一次性的查询完就关 串口就是一直打开做判断即可 条件不足就不执行就完了·· 我是不会在打开和关闭之中写 return;的
    lrxiao
        31
    lrxiao  
       2017-04-24 13:35:25 +08:00
    @dingz 啊我语气太重了..
    limhiaoing
        32
    limhiaoing  
       2017-04-24 13:57:04 +08:00 via iPhone   ❤️ 1
    @rozbo
    auto a = std::make_unique<char[]>(0x1000);
    其实现代 C++不需要手动写任何的 new 和 delete 也能把内存资源管理得很好。
    举个例子 https://github.com/lxrite/DawnPlayer 是我写的一个 flv demuxer 涉及到大量的内存分配和释放,但是 C++部分的代码一个 delete 都没有写。
    rozbo
        33
    rozbo  
       2017-04-24 14:27:53 +08:00
    @limhiaoing
    @lrxiao
    @coderluan
    @gamexg
    @dingz
    提到 c++的内存,统一返回确实不是唯一的解决方案。
    但是各种对象具柄怎么办? c 语言怎么办?数据库连接怎么办?
    另外我问的是 char * a=(char*) malloc(0x1000);这种怎么办,请正视问题,不要改写这一段。
    难道你们写代码的时候,真的是从来不用 malloc ?
    limhiaoing
        34
    limhiaoing  
       2017-04-24 14:39:46 +08:00 via iPhone
    @rozbo
    http://en.cppreference.com/w/cpp/memory/unique_ptr
    sto::unique_ptr 的 deleter 是可定制的,你想释放其他资源定制下 deleter 就可以了。
    另外 不明白你是什么需求非得用 malloc 而不能用 std::make_unique 。
    limhiaoing
        35
    limhiaoing  
       2017-04-24 14:41:48 +08:00 via iPhone
    上面的链接有用 std::unique_ptr 管理文件资源的例子。
    limhiaoing
        36
    limhiaoing  
       2017-04-24 14:43:59 +08:00 via iPhone
    @rozbo
    忘了回答你最后一句了,我写代码确实从来不用 malloc 。
    Ouyangan
        37
    Ouyangan  
       2017-04-24 14:50:32 +08:00
    java-->遇到要返回的话直接就返回 ,避免后面很多 if 判断
    htfy96
        38
    htfy96  
       2017-04-24 14:59:36 +08:00
    @rozbo
    1. 对象句柄封装下 unique_ptr
    2. C 本来就是另一种语言
    3. 数据库连接也可以啊:参见 https://github.com/cruisercoder/cppstddb/blob/master/src/cppstddb/mysql/database.h#L104
    4. auto a = std::unique_ptr<char[]>(new char[0x1000])
    5. 基本不用
    sciooga
        39
    sciooga  
       2017-04-24 15:10:45 +08:00 via Android
    提前返回一般有更好的可读性
    usbuild
        40
    usbuild  
       2017-04-24 16:04:36 +08:00
    C++中不需要 malloc ,甚至有些情况下连 new/delete 都不需要,一般 vector 就够用了。
    jhdxr
        41
    jhdxr  
       2017-04-24 16:07:26 +08:00
    php/java: 取决于具体情况,但能够提前返回的时候绝对不会硬拖着到最后再统一返回(例如函数开头的异常情况判断)。
    至于资源回收,不熟悉 c++不敢随便说话,但是 php/java 都可以通过 finally 来解决
    Observer42
        42
    Observer42  
       2017-04-24 16:10:27 +08:00
    提前返回,减少嵌套层数,参照 4 楼的

    c 没怎么写过, c++有 RAII ,而且自 11 以后 unique_ptr 可以少很多 new/delete ,性能基本不会是问题
    geelaw
        43
    geelaw  
       2017-04-24 16:13:13 +08:00
    ……不用 malloc 是不是没写过性能要求高的程序哇……用 vector 的更是,你们很理论计算机科学,常数统统扔掉

    @rozbo 这个问题很简单,你可以

    char *a = (char *)malloc(0x1000);

    struct _free_a_tag
    {
    char const *ptr;
    _free_a_tag(char const *p) : ptr{p} { }
    ~_free_a_tag() { free(ptr); }
    } _free_a = a;

    用得多就 refactor 一个结构到外面去咯。

    如果你有一个 handle ,你可以写一个关闭 handle 的类似的玩意儿
    geelaw
        44
    geelaw  
       2017-04-24 16:13:38 +08:00
    @geelaw 更正:把 char const * 改成 char * const
    zacard
        45
    zacard  
       2017-04-24 16:27:54 +08:00
    尽量提前返回
    limhiaoing
        46
    limhiaoing  
       2017-04-24 16:27:57 +08:00 via iPhone   ❤️ 2
    @geelaw
    真不知道说你什么好,性能高必须用 malloc ?
    高性能程序都是用内存池的好吗?
    另外 vector 有性能问题是你不会用。
    jarlyyn
        47
    jarlyyn  
       2017-04-24 16:44:36 +08:00
    看这个帖子发现 go 的 defer 真是疼程序员
    changwei
        48
    changwei  
       2017-04-24 16:47:26 +08:00 via Android
    看是写什么代码,像 golang 等语言有 defer 关键词,各种提前返回可能会导致逻辑上的混乱,还是统一返回比较好。毕竟偷懒和优雅二者不可得兼。
    geelaw
        49
    geelaw  
       2017-04-24 17:13:39 +08:00
    @limhiaoing 我应该更正为“只用 new/delete 和 vector<T>”,本意当然不是只能用 malloc ,比如你还可以 calloc ,或者 VirtualAlloc 。

    另外如果希望提高 vector 的性能,需要写自己的 allocator (避免愚蠢的 new ,通常的实现),然后调用一次 reserve ,再祈祷编译器可以帮你把那些抽象都返朴归真(好在大多数编译器都可以搞定);反正我是不觉得在性能密集场景下 vector 比脱掉 vector 高明多少——如果要求期间不发生内存重新分配,那 vector 还有什么价值呢?
    xiubin
        50
    xiubin  
       2017-04-24 18:14:36 +08:00
    不说使用场景的都是耍流氓吧

    判断合法,不合法直接返回 nil :
    if (para1 == nil) return
    if (para2 == nil) return
    if (para3 == nil) return

    // para ...

    return ...

    根据不同条件会有不同结果:

    id result;
    if ..
    result = ..
    else if ..
    result = ..

    return result
    ldp940622
        51
    ldp940622  
       2017-04-24 18:14:54 +08:00
    自从使用了 swift ,就比较习惯使用 gurad 和 defer 了
    sagaxu
        52
    sagaxu  
       2017-04-24 18:21:58 +08:00
    @jhdxr Java7 开始都用 try-with-resource 了,不用在 finally 里 close
    sagaxu
        53
    sagaxu  
       2017-04-24 18:24:34 +08:00
    @changwei 你知道 defer 是干什么用的吗?就是为了提前返回的时候不让你忘记清理工作。有 defer 反而更适合提前返回。
    xiaowangge
        54
    xiaowangge  
       2017-04-24 18:53:05 +08:00
    游戏行业的表示,一般来说,编码风格都是强制的提前返回 :-)
    bumz
        55
    bumz  
       2017-04-24 18:57:12 +08:00
    提前返回
    jhdxr
        56
    jhdxr  
       2017-04-24 19:25:49 +08:00
    @sagaxu 多谢,之前还真没注意到这个特性。。。比 finally 写着优雅多了。
    0915240
        57
    0915240  
       2017-04-24 20:03:41 +08:00
    orderc
        58
    orderc  
       2017-04-24 20:21:25 +08:00 via iPhone
    提前返回,不用加一大堆 if else
    mb4555
        59
    mb4555  
       2017-04-24 20:45:22 +08:00 via Android
    提前
    ldp940622
        60
    ldp940622  
       2017-04-24 21:00:44 +08:00 via iPhone
    @ldp940622 gurad -> guard
    ivvei
        61
    ivvei  
       2017-04-24 21:09:16 +08:00 via Android
    看心情吧…
    shijingshijing
        62
    shijingshijing  
       2017-04-24 21:43:40 +08:00
    嵌入式里面,这种直接是真值表套状态机的, Guard Clauses 写代码的时候是爽了,测试和 MC/DC 覆盖率分析的时候分分钟教做人。
    kongkongyzt
        63
    kongkongyzt  
       2017-04-24 22:15:53 +08:00 via Android
    提前返回,并且尽量提前返回。
    mingyun
        64
    mingyun  
       2017-04-24 22:50:33 +08:00
    尽早 return
    zoffy
        65
    zoffy  
       2017-04-24 22:52:23 +08:00
    提前
    loveyu
        66
    loveyu  
       2017-04-25 00:49:47 +08:00
    提前吧,至少把错误判断提前,如果觉得复杂就 try_catch ,否则真的很难理解。
    RqPS6rhmP3Nyn3Tm
        67
    RqPS6rhmP3Nyn3Tm  
       2017-04-25 05:13:42 +08:00 via iPad
    我这里禁止提前返回,因为会要求 proof 。
    MajorAdam
        68
    MajorAdam  
       2017-04-25 09:44:10 +08:00
    肯定提前
    fanqsh123
        69
    fanqsh123  
       2017-04-25 09:56:23 +08:00
    int luaGetStringMd5(lua_State* L)
    {
    int nResult = 0;
    BOOL bRet = false;
    char szMd5[64];
    const char* pszString = NULL;

    TRY_GETSTRING(pszString, 1);

    bRet = GetStringMD5(szMd5, pszString);
    XYLOG_FAILED_JUMP(bRet);

    lua_pushstring(L, szMd5);
    ++nResult;

    Exit0:
    return nResult;
    }


    goto 挺好用的
    dogfeet
        70
    dogfeet  
       2017-04-25 10:28:38 +08:00
    @geelaw 既然都是自己 malloc 了,那操作这块内存的时候大小一定是自己很自信了。实在看不出 vector<uchar> 额定大小再操作 data 比你上面的写法性能低效在哪里,至少肯定不会是比 malloc 低。
    lance26
        71
    lance26  
       2017-04-25 10:38:41 +08:00
    @shijingshijing 可以给段代码看看吗?想学习下
    wayslog
        72
    wayslog  
       2017-04-25 12:33:38 +08:00 via Android
    @rozbo 看来你不用 RAII 啊…都什么年代了写 C++还手动管理内存?当然了,写 C 的无解了…
    rogerchen
        73
    rogerchen  
       2017-04-25 12:42:34 +08:00 via Android
    @wayslog
    一看那哥们就是写 C 的,连 C with class 都算不上, RAII 管理外部资源这种常识都不知道。
    allgy
        74
    allgy  
       2017-04-25 14:07:03 +08:00
    提前返回
    geelaw
        75
    geelaw  
       2017-04-25 14:19:41 +08:00
    @dogfeet 啊我突然发现“ vector 的价值”了 - - 就是自动回收内存(为啥说着说着连最初的目的都忘掉了逃
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5652 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 39ms · UTC 02:39 · PVG 10:39 · LAX 18:39 · JFK 21:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.