编程过程中, 你是习惯提前返回, 还是统一返回。 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
banli
V2EX    问与答

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

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

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

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

    你觉得呢?

    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
    @ingz
    提到 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     Solana     5201 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    VERSION: 3.9.8.5 39ms UTC 07:30 PVG 15:30 LAX 00:30 JFK 03:30
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86