我经常在一个方法的开头有几个语句来返回“简单”的情况。例如,这个:

public void DoStuff(Foo foo)

{

if (foo != null)

{

...

}

}

...可以像这样更具可读性(恕我直言):

public void DoStuff(Foo foo)

{

if (foo == null) return;

...

}

所以,是的,我认为从函数/方法中获得多个“退出点”是很好的。

没有人提到或引用Code Complete,所以我会这样做。

17.1回归

最小化每个例程中的返回数。理解一个例程是比较困难的,如果从底部读取它,你就不会意识到它返回到某个地方的可能性。

在增强可读性时使用返回。在某些例程中,一旦知道答案,就要立即将其返回到调用例程。如果以不需要任何清理的方式定义例程,则不立即返回意味着您必须编写更多代码。

我会说任意对多个出口点进行任意决定是非常不明智的,因为我发现这种技术在实践中一遍又一遍地有用,事实上我为了清楚起见经常将现有代码重构为多个出口点。我们可以比较这两种方法: -

string fooBar(string s, int? i) {

string ret = "";

if(!string.IsNullOrEmpty(s) && i != null) {

var res = someFunction(s, i);

bool passed = true;

foreach(var r in res) {

if(!r.Passed) {

passed = false;

break;

}

}

if(passed) {

// Rest of code...

}

}

return ret;

}

将此与允许多个出口点的代码进行比较: -

string fooBar(string s, int? i) {

var ret = "";

if(string.IsNullOrEmpty(s) || i == null) return null;

var res = someFunction(s, i);

foreach(var r in res) {

if(!r.Passed) return null;

}

// Rest of code...

return ret;

}

我认为后者相当清楚。据我所知,多个退出点的批评现在是一个相当陈旧的观点。

我目前正在开发一个代码库,其中有两个人在盲目地订阅“单点退出”理论,我可以告诉你,根据经验,这是一个可怕的可怕做法。它使代码非常难以维护,我会告诉你原因。

有了“单点退出”理论,您不可避免地会遇到如下代码:

function()

{

HRESULT error = S_OK;

if(SUCCEEDED(Operation1()))

{

if(SUCCEEDED(Operation2()))

{

if(SUCCEEDED(Operation3()))

{

if(SUCCEEDED(Operation4()))

{

}

else

{

error = OPERATION4FAILED;

}

}

else

{

error = OPERATION3FAILED;

}

}

else

{

error = OPERATION2FAILED;

}

}

else

{

error = OPERATION1FAILED;

}

return error;

}

这不仅使代码很难遵循,而且现在稍后说你需要返回并在1到2之间添加一个操作。你必须缩进整个怪异功能,祝你好运你的if / else条件和大括号是否正确匹配。

此方法使代码维护极其困难且容易出错。

结构化编程说你应该每个函数只有一个return语句。这是为了限制复杂性。许多人如Martin Fowler认为用多个return语句编写函数更简单。他在他写的经典重构书中提出了这个论点。如果你遵循他的其他建议并编写小函数,这很有效。我同意这种观点,只有严格的结构化编程纯粹主义者遵守每个函数的单个返回语句。

正如Kent Beck在实施模式中讨论保护条款时所做的那样,使例程有一个单一的入口和出口点......

“是为了防止混乱

当进出许多人时

同一例程中的位置。它做了

应用于FORTRAN时的良好意义

编写汇编语言程序

甚至还有很多全球数据

了解哪些陈述

执行起来很辛苦......用小方法和大多数本地数据,这是不必要的保守。“

我发现一个用guard子句编写的函数比一堆长的嵌套的if then else语句更容易理解。

在没有副作用的函数中,没有充分的理由要有多个返回,你应该以函数式编写它们。在具有副作用的方法中,事物更顺序(时间索引),因此您使用return语句作为命令停止执行,以命令式方式编写。

换句话说,如果可能的话,赞成这种风格

return a > 0 ?

positively(a):

negatively(a);

在此

if (a > 0)

return positively(a);

else

return negatively(a);

如果您发现自己编写了几层嵌套条件,那么可能有一种方法可以使用谓词列表进行重构。如果你发现你的ifs和elses在语法上相差很远,你可能想把它分解成更小的函数。跨越多个屏幕文本的条件块很难阅读。

没有适用于每种语言的硬性规则。像一个返回语句之类的东西不会使你的代码变好。但是好的代码往往会让你以这种方式编写你的函数。

我已经在C ++的编码标准中看到它,这是C语言的遗留问题,好像你没有RAII或其他自动内存管理那么你必须为每次返回清理,这或者意味着剪切和粘贴清理或goto(逻辑上与托管语言中的'finally'相同),两者都被认为是不良形式。如果你的做法是在C ++或其他自动内存系统中使用智能指针和集合,那么它就没有充分的理由,而且它完全取决于可读性,而更多的是判断调用。

我倾向于认为函数中间的返回语句很糟糕。你可以使用返回来在函数的顶部构建一些guard子句,当然告诉编译器在函数结束时返回什么没有问题,但是函数中间的返回很容易错过并且可以使功能更难解释。

有没有充分的理由为什么在函数中只有一个return语句是更好的做法?

是的,有:

单一出口点是确定后置条件的绝佳场所。

能够在函数末尾的一个返回上放置调试器断点通常很有用。

回报越少意味着复杂性越低。线性代码通常更容易理解。

如果试图将函数简化为单个返回会导致复杂性,那么这就是重构更小,更通用,更易于理解的函数的动机。

如果您使用的是没有析构函数的语言,或者如果您不使用RAII,那么单次返回会减少您必须清理的地点数量。

某些语言需要单个退出点(例如,Pascal和Eiffel)。

这个问题通常被认为是多个回报或深度嵌套的if语句之间的错误二分法。几乎总有第三种解决方案非常线性(没有深度嵌套),只有一个出口点。

更新:显然,MISRA指南也促进单一退出。

要明确的是,我并不是说多次退货总是错的。但是考虑到其他方面的等效解决方案,有很多理由可以选择单一回报。

具有单个出口点确实提供了调试的优势,因为它允许您在函数末尾设置单个断点,以查看实际将返回的值。

一般来说,我尝试只有一个函数的退出点。然而,有时这样做实际上最终会创建一个比必要的更复杂的函数体,在这种情况下,最好有多个出口点。它必须是基于由此产生的复杂性的“判断调用”,但目标应该是尽可能少的出口点而不牺牲复杂性和可理解性。

不,因为我们不再生活在20世纪70年代了。如果你的功能足够长,多次返回是一个问题,那就太长了。

(除了事实上,具有例外的语言中的任何多行函数无论如何都会有多个出口点。)

我倾向于单一退出,除非它真的让事情复杂化。我发现在某些情况下,多个存在点可以掩盖其他更重要的设计问题:

public void DoStuff(Foo foo)

{

if (foo == null) return;

}

看到这段代码后,我会马上问:

'foo'永远是空的吗?

如果是这样,'DoStuff'有多少客户端使用null'foo'调用该函数?

根据这些问题的答案,可能就是这样

检查是没有意义的,因为它永远不是真的(即它应该是一个断言)

检查很少是真的,所以最好改变那些特定的调用函数,因为他们应该采取其他一些行动。

在上述两种情况下,代码都可以通过断言重新编写,以确保'foo'永远不为null并且相关的调用者发生了变化。

还有另外两个原因(具体我认为是C ++代码),其中存在多个可能实际上具有负面影响。它们是代码大小和编译器优化。

函数出口范围内的非POD C ++对象将调用其析构函数。如果有多个返回语句,则可能是范围内有不同的对象,因此要调用的析构函数列表将不同。因此编译器需要为每个return语句生成代码:

void foo (int i, int j) {

A a;

if (i > 0) {

B b;

return ; // Call dtor for 'b' followed by 'a'

}

if (i == j) {

C c;

B b;

return ; // Call dtor for 'b', 'c' and then 'a'

}

return 'a' // Call dtor for 'a'

}

如果代码大小是一个问题 - 那么这可能是值得避免的。

另一个问题涉及“命名返回值优化”(又名Copy Elision,ISO C ++ '03 12.8 / 15)。 C ++允许实现跳过调用复制构造函数,如果它可以:

A foo () {

A a1;

// do something

return a1;

}

void bar () {

A a2 ( foo() );

}

只需按原样执行代码,对象'a1'在'foo'中构造,然后将调用其复制构造来构造'a2'。但是,copy elision允许编译器在堆栈的同一位置构造'a1'作为'a2'。因此,当函数返回时,不需要“复制”对象。

多个出口点使编译器在尝试检测到这一点时的工作变得复杂,并且至少对于VC ++的相对较新版本,在函数体具有多个返回的情况下不进行优化。有关详细信息,请参阅Visual C ++ 2005中的命名返回值优化。

具有单个退出点可降低Cyclomatic Complexity,因此理论上可以降低在更改代码时将错误引入代码的可能性。然而,实践往往表明需要更务实的方法。因此,我倾向于只有一个退出点,但如果可读性更高,则允许我的代码有几个。

我强迫自己只使用一个return语句,因为它在某种意义上会产生代码气味。让我解释:

function isCorrect($param1, $param2, $param3) {

$toret = false;

if ($param1 != $param2) {

if ($param1 == ($param3 * 2)) {

if ($param2 == ($param3 / 3)) {

$toret = true;

} else {

$error = 'Error 3';

}

} else {

$error = 'Error 2';

}

} else {

$error = 'Error 1';

}

return $toret;

}

(条件是arbritary ......)

条件越多,函数越大,读取的难度就越大。因此,如果你对代码气味很熟悉,你就会意识到这一点,并希望重构代码。两种可能的解决方案是

多次退货

重构为单独的函数

多次退货

function isCorrect($param1, $param2, $param3) {

if ($param1 == $param2) { $error = 'Error 1'; return false; }

if ($param1 != ($param3 * 2)) { $error = 'Error 2'; return false; }

if ($param2 != ($param3 / 3)) { $error = 'Error 3'; return false; }

return true;

}

单独的功能

function isEqual($param1, $param2) {

return $param1 == $param2;

}

function isDouble($param1, $param2) {

return $param1 == ($param2 * 2);

}

function isThird($param1, $param2) {

return $param1 == ($param2 / 3);

}

function isCorrect($param1, $param2, $param3) {

return !isEqual($param1, $param2)

&& isDouble($param1, $param3)

&& isThird($param2, $param3);

}

当然,它更长,有点凌乱,但在这种方式重构功能的过程中,我们已经

创建了许多可重用的函数,

使功能更具人性化,并且

功能的重点是价值为何正确。

我会说你应该有尽可能多的,或任何使代码更清洁(如保护条款)。

我个人从来没有听说过/看到任何“最佳实践”说你应该只有一个回复声明。

在大多数情况下,我倾向于根据逻辑路径尽快退出函数(保护子句就是一个很好的例子)。

我相信多次返回通常都很好(在我用C#编写的代码中)。单回归风格是C的保留。但你可能不是用C编码。

对于所有编程语言中的方法,没有法律只要求一个出口点。有些人坚持这种风格的优越性,有时他们将其提升为“规则”或“法律”,但这种信念并没有任何证据或研究支持。

C代码中不止一种返回样式可能是一种坏习惯,其中必须显式地取消分配资源,但是Java,C#,Python或JavaScript等语言具有自动垃圾收集和try..finally块(以及using块)等构造在C#)中,这个参数不适用 - 在这些语言中,需要集中的手动资源释放是非常罕见的。

有些情况下,单个返回更易读,而有些情况则不可读。看看它是否减少了代码行数,使逻辑更清晰或减少了大括号和缩进或临时变量的数量。

因此,使用尽可能多的回报以适合您的艺术感受,因为它是布局和可读性问题,而不是技术问题。

我在博客上已经详细讨论了这个问题。

有一个单一的退出点有很好的说法,就像对不可避免的“箭头”编程有不好的说法。

如果在输入验证或资源分配期间使用多个退出点,我会尝试将所有“错误退出”非常明显地放在函数的顶部。

“SSDSLPedia”的Spartan Programming文章和“Portland Pattern Repository Wiki”的单一功能出口点文章都有一些有见地的论据。当然,还有这篇文章需要考虑。

如果你真的想要一个退出点(在任何非异常启用的语言中),例如为了在一个地方释放资源,我发现goto的谨慎应用是好的;例如,看看这个相当人为的例子(压缩以节省屏幕空间):

int f(int y) {

int value = -1;

void *data = NULL;

if (y < 0)

goto clean;

if ((data = malloc(123)) == NULL)

goto clean;

/* More code */

value = 1;

clean:

free(data);

return value;

}

就个人而言,我一般不喜欢箭头编程,而不喜欢多个退出点,尽管两者在正确应用时都很有用。当然,最好的是将程序结构化为既不需要也不需要。将您的功能分解为多个块通常有帮助:)

虽然在这样做的时候,我发现我最终会得到多个出口点,就像在这个例子中一样,一些较大的函数被分解为几个较小的函数:

int g(int y) {

value = 0;

if ((value = g0(y, value)) == -1)

return -1;

if ((value = g1(y, value)) == -1)

return -1;

return g2(y, value);

}

根据项目或编码指南,大多数锅炉板代码可以用宏替换。作为旁注,以这种方式将其分解使得函数g0,g1,g2非常容易单独测试。

显然,在一个支持OO和异常的语言中,我不会像那样使用if语句(或者根本不使用if,如果我能用很少的努力来逃避它),代码就会更加清晰。而非箭头。大多数非最终回报可能都是例外。

简而言之;

很少有回报比许多回报更好

不止一次回归比巨箭更好,保护条款通常都可以。

在可能的情况下,例外可能/应该取代大多数“保护条款”。

你知道这句谚语 - 美丽在旁观者的眼中。

有些人发誓NetBeans,有些人发誓IntelliJ IDEA,有些人用Python发誓,有些人用PHP发誓。

在一些商店,如果你坚持这样做,你可能会失去工作:

public void hello()

{

if (....)

{

....

}

}

问题在于可见性和可维护性。

我沉迷于使用布尔代数来减少和简化逻辑和状态机的使用。然而,过去有些同事认为我在编码中使用“数学技术”是不合适的,因为它不可见和可维护。这将是一个糟糕的做法。对不起的人,我使用的技术对我来说非常明显和可维护 - 因为当我六个月后回到代码时,我会清楚地理解代码,而不是看到一堆众所周知的意大利面条。

嘿伙计(就像以前的客户曾经说过的那样)做你想做的事,只要你知道如何解决它,当我需要你解决它。

我记得20年前,我的一位同事因雇用今天所谓的敏捷发展战略而被解雇。他有一个细致的增量计划。但他的经理却对他大吼大叫“你无法逐步向用户发布功能!你必须坚持使用瀑布。”他对经理的回应是增量开发会更准确地满足客户的需求。他相信为客户的需求而开发,但经理相信编码符合“客户的要求”。

我们经常因打破数据规范化,MVP和MVC边界而感到内疚。我们内联而不是构造一个函数。我们采取捷径。

就个人而言,我认为PHP是不好的做法,但我知道什么。所有的理论论点都归结为尝试实现一套规则

质量=精度,可维护性

和盈利能力。

所有其他规则都淡入背景。当然,这条规则永远不会消退:

懒惰是善的美德

程序员。

我倾向于使用保护条款提前返回,否则在方法结束时退出。单个进入和退出规则具有历史意义,在处理具有多个返回(以及许多缺陷)的单个C ++方法的10个A4页面的遗留代码时尤其有用。最近,公认的良好做法是保持方法较小,这使得多个出口对理解的阻抗较小。在下面从上面复制的Kronoz示例中,问题是//其余代码中出现的内容......?:

void string fooBar(string s, int? i) {

if(string.IsNullOrEmpty(s) || i == null) return null;

var res = someFunction(s, i);

foreach(var r in res) {

if(!r.Passed) return null;

}

// Rest of code...

return ret;

}

我意识到这个例子有点做作,但我很想将foreach循环重构为一个LINQ语句,然后可以将其视为一个保护条款。同样,在一个人为的例子中,代码的意图并不明显,someFunction()可能有一些其他的副作用,或者结果可能会在//其余代码中使用....

if (string.IsNullOrEmpty(s) || i == null) return null;

if (someFunction(s, i).Any(r => !r.Passed)) return null;

给出以下重构函数:

void string fooBar(string s, int? i) {

if (string.IsNullOrEmpty(s) || i == null) return null;

if (someFunction(s, i).Any(r => !r.Passed)) return null;

// Rest of code...

return ret;

}

我能想到的一个很好的理由是代码维护:你只有一个退出点。如果你想改变结果的格式,......,它实现起来要简单得多。另外,为了调试,你可以在那里坚持一个断点:)

话虽如此,我曾经不得不在一个库中工作,在这个库中,编码标准强加了“每个函数一个返回语句”,我发现它非常难。我写了很多数值计算代码,经常有“特殊情况”,所以代码最终很难遵循......

多个出口点适用于足够小的功能 - 也就是说,可以在一个屏幕长度上查看整个功能。如果冗长的功能同样包括多个出口点,则表明该功能可以进一步切断。

这就是说除非绝对必要,否则我会避免多次退出功能。我感到很多错误的痛苦,这些错误是由于在更复杂的功能中某些模糊的线路中的一些偏离返回。

我已经使用了可怕的编码标准,迫使你只有一个退出路径,结果几乎总是非结构化的意大利面条,如果这个功能不算什么微不足道的事情 - 你最终会有很多休息时间而且会继续阻碍你。

单一退出点 - 所有其他条件相同 - 使代码更具可读性。

但有一个问题:流行建筑

resulttype res;

if if if...

return res;

是假的,“res =”并不比“返回”好多少。它有单个return语句,但函数实际结束的多个点。

如果你有多个返回函数(或“res =”s),通常最好将它分成几个带有单个退出点的较小函数。

我通常的策略是在函数末尾只有一个return语句,除非通过添加更多代码来大大减少代码的复杂性。事实上,我更喜欢Eiffel,它通过没有return语句强制执行唯一的返回规则(只有一个自动创建的'result'变量来放置你的结果)。

当然有些情况下代码可以通过多次返回比没有它们的明显版本更清晰。有人可能会争辩说,如果你的函数太复杂而没有多个return语句就无法理解,那么就需要更多的返工,但有时候对这些事情务实是好的。

如果您最终得到的回报不止一些,那么您的代码可能会出现问题。否则我会同意,有时能够从子例程中的多个位置返回是很好的,特别是当它使代码更清晰时。

Perl 6:不好的例子

sub Int_to_String( Int i ){

given( i ){

when 0 { return "zero" }

when 1 { return "one" }

when 2 { return "two" }

when 3 { return "three" }

when 4 { return "four" }

...

default { return undef }

}

}

会更好地写这样的

Perl 6:很好的例子

@Int_to_String = qw{

zero

one

two

three

four

...

}

sub Int_to_String( Int i ){

return undef if i < 0;

return undef unless i < @Int_to_String.length;

return @Int_to_String[i]

}

请注意,这只是一个简单的例子

我最后投票给单回报作为指导。这有助于常见的代码清理处理...例如,看看下面的代码......

void ProcessMyFile (char *szFileName)

{

FILE *fp = NULL;

char *pbyBuffer = NULL:

do {

fp = fopen (szFileName, "r");

if (NULL == fp) {

break;

}

pbyBuffer = malloc (__SOME__SIZE___);

if (NULL == pbyBuffer) {

break;

}

/*** Do some processing with file ***/

} while (0);

if (pbyBuffer) {

free (pbyBuffer);

}

if (fp) {

fclose (fp);

}

}

这可能是一个不同寻常的观点,但我认为任何认为多个返回语句都受到青睐的人都不必在仅支持4个硬件断点的微处理器上使用调试器。 ;-)

虽然“箭头代码”的问题是完全正确的,但是在使用多个返回语句时似乎消失的一个问题是在使用调试器的情况下。您没有方便的全部位置来设置断点以保证您将看到退出并因此返回条件。

函数中返回的语句越多,该方法的复杂性就越高。如果您发现自己想知道是否有太多的返回语句,您可能想问问自己,该函数中是否有太多代码行。

但是,不是,一个/多个return语句没有错。在某些语言中,它比其他语言(C)更好(C ++)。

python中一个函数只允许有一条return语句_一个函数应该只有一个return语句吗?相关推荐

  1. python 中的for i in range()的使用(for _ in range())

    for i in range(n) range(n)输出的区间为 [ 0 , n − 1 ] [0, n - 1][0,n−1],代码如下 for i in range(5):     print(i ...

  2. 当 Python 中混进一只薛定谔的猫……

    作者 | 豌豆花下猫 责编 | 胡巍巍 Python 是一门强大的动态语言,那动态体现在哪里,强大又体现在哪里呢? 除了好的方面,Python 的动态性是否还藏着一些使用陷阱呢,有没有办法识别与避免呢 ...

  3. python中用于释放类占用的资源的方法是()_编写一个简易计算器,要求根据输入的数字和四则运算符号,计算运算结果并输出。_学小易找答案...

    [简答题]20191220 课前作业 新工作页4.1的3-5-3页的填空题,参考教材P135-P144 [简答题]AutoCAD改编视图,尽量不用虚线 1. 主视图采用局部剖,表达右上角小圆筒(及孔) ...

  4. python 中主线程结束 子线程还在运行么_「干货」python线程笔记

    引言&动机 考虑一下这个场景,我们有10000条数据需要处理,处理每条数据需要花费1秒,但读取数据只需要0.1秒,每条数据互不干扰.该如何执行才能花费时间最短呢? 在多线程(MT)编程出现之前 ...

  5. python中if有几种使用方式_在 Python 中可以使用 if 作为变量名。 (2.0分)_学小易找答案...

    [判断题]尽管可以使用 import 语句一次导入任意多个标准库或扩展库,但是仍建议每次只导入一个标准库或扩展库. (2.0分) [简答题]写出 Python 运算符 & 的两种功能? (5. ...

  6. 用python显示出所有能被17整除的三位数_小学弟敲了一个晚上整理的Python初学者习题,给你们学习!...

    装饰器以及剩余的定义函数一边做题一边编译器里敲,已经天亮了沉迷python,无法自拔希望初学者认真做题,总会有帮助的,在里面我每道题尽可能用多种方法解! 如果你感觉学不会?莫慌,小编推荐大家加入群, ...

  7. 在python语言中不能作为变量名的是什么_4、 在 Python 中可以使用 if 作为变量名。 (1.0分)_学小易找答案...

    [判断题]2. Python 变量使用前必须先声明 , 并且一旦声明就不能在当前作用域内改变其类型. (1.0分) [填空题]Python语句''.join(list('hello world!')) ...

  8. 关于python中的复数类型、下列说法错误的是_关于 Python中的复数,下列说法错误的是( )_学小易找答案...

    [单选题]下列关于运算符优先级的描述中,错误的是 . [判断题]Java 语言中定义符号常量使用final关键字.( ) [判断题]捕获异常时try语句后面通常跟有一个或多个catch()方法用来处理 ...

  9. c语言中接口和函数的区别,接口与类的关系_接口函数和调用函数有什么区别

    描述 接口与类的关系 类与接口是两个概念,不能混淆. 接口概念 程序接口是操作系统为用户提供的两类接口之一,编程人员在程序中通过程序接口来请求操作系统提供服务.面向过程语言最基本的单元是过程和函数. ...

最新文章

  1. 2022-2028年中国化妆品OEM行业市场发展规模及前瞻分析报告
  2. java nio Selector (新IO)分析
  3. aspose-cells 表合并
  4. android c# websocket
  5. D3可视化:(1)初次见面,SVG与D3的魅力
  6. 怎么样才算是精通 Python?
  7. Tarjan+topsort(DP)【P3387】 [模板]缩点
  8. sklearn 自定义函数转化器FunctionTransformer使用
  9. js分页展示控件,传入简单参数就能使用的分页效果控件
  10. linux系统下的打印机驱动下载,为 Linux 选择打印机 | Linux 中国
  11. 电脑无法安装SecoClient
  12. ABC三类地址及其子网掩码
  13. 下载并安装IDA Pro 7.0
  14. 计算机专业女生进电网,考入华北电力大学计算机专业,无缘国家电网,这是为什么?...
  15. linux 硬盘合并使用方法,Linux硬盘合并的实现代码
  16. linux sql server 迁移,SQL Server2008 实例数据迁移步骤
  17. 一个屌丝程序员的青春(五一)
  18. 虚拟现实(VR)、增强现实(AR)、混合现实(MR)
  19. 【JY】有限单元分析的常见问题及单元选择
  20. [Solved] Pycharm 提示Unresolved reference

热门文章

  1. IOS 设计模式 桥接模式(bridge)
  2. 2023搜狐科技峰会结束 白春礼刘韵洁武向平等院士解读科技新格局
  3. 向量的点乘与叉乘概念理解以及C++代码实现
  4. ROS+Gmapping+镭神智能激光雷达应用测试(N301-01B)
  5. mysql(英语不好看)
  6. Python报错UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte 0x80 in position 10
  7. (转)Docker镜像结构原理
  8. 实际开发中自动跳转功能
  9. c语言msgpack简单用例
  10. 【微信技术-微信公众号】-------- 实现拍照或选择相册接口:wx.chooseImage的使用实例[JS-SDK]