About PHP:”If”和”switch”

“if” versus “switch”

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
is “else if” faster than “switch() case” ?

我最近遇到了很多情况,其中我有非常简单的条件,需要分支应用程序流。"最简单"的意思是完成我正在做的事情只是一个简单的老if/elseif声明:

1
2
3
4
5
6
7
if($value =="foo") {
    // ...
} elseif($value =="bar") {
    // ...
} elseif($value =="asdf" || $value =="qwerty") {
    // ...
}

…但我也在考虑:

1
2
3
4
5
6
7
8
9
10
11
switch($value) {
    case"foo":
        // ...
        break;
    case"bar":
        // ...
        break;
    case"qwer":
    case"asdf":
        // ...
}

这看起来有点不太可读,但也许它更具性能?但是,当条件中有越来越多的"或"表达式时,switch语句似乎更可读和有用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
switch($value) {
    case"foo":
        // ...
        break;
    case"bar":
    case"baz":
    case"sup":
        // ...
        break;
    case"abc":
    case"def":
    case"ghi":
        // ...
        break;
    case"qwer":
    case"asdf":
        // ...
}

我还看到了使用数组和函数分支代码流的选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
function branch_xyz() {/* ... */}
function branch_abc() {/* ... */}
function branch_def() {/* ... */}

$branches = array(
   "xyz"=>"branch_xyz",
   "abc"=>"branch_abc",
   "def"=>"branch_def"
);
if(isset($branches[$value])) {
    $fname = $branches[$value];
    $fname();
}

最后一个选项也可能具有跨多个文件分发的好处,尽管它非常难看。

在性能、可读性和易用性方面,您认为哪一个在权衡最少的情况下最具优势?


我知道,微观优化是不好的。但尽管我很好奇,我还是用这个脚本做了一些基准测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
$numof = 100000;
$file = fopen('benchmark.php', 'w');
if (!$file) die('file error');
fwrite($file, '[cc lang="php"]<?php' ."
"
. 'echo $i = $_GET[\'i\'],"
";'
."
"
);

fwrite($file,
'$start = microtime(true);
if ($i == 0) {}'
."
"
);
for ($i = 1; $i < $numof; ++$i) {
    fwrite($file, 'elseif($i == '.$i.') {}'."
"
);
}
fwrite($file,
'echo \'elseif took: \', microtime(true) - $start,"
";'
."
"
);

fwrite($file,
'$start = microtime(true);
switch($i) {'
."
"
);
for ($i = 1; $i < $numof; ++$i) {
    fwrite($file, 'case '.$i.': break;'."
"
);
}
fwrite($file,
'}
echo \'switch took: \', microtime(true) - $start,"
";'
."
"
);

结果数据(对于numof=100000):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
i: 0
elseif took: 6.2942504882812E-5
switch took: 3.504753112793E-5

i: 10
elseif took: 6.4849853515625E-5
switch took: 4.3869018554688E-5

i: 100
elseif took: 0.00014805793762207
switch took: 0.00011801719665527

i: 1000
elseif took: 0.00069785118103027
switch took: 0.00098896026611328

i: 10000
elseif took: 0.0059938430786133
switch took: 0.0074150562286377

i: 100000 (first non-existing offset)
elseif took: 0.043318033218384
switch took: 0.075783014297485

我已经用php 5.3.1或5.3.2在我的旧的慢速Windows机器上运行了这个脚本,不知道该怎么做。


就我个人而言,我觉得这个开关更可读。原因如下:

1
2
3
4
5
if ($foo == 'bar') {
} elseif ($foo == 'baz') {
} elseif ($foo == 'buz') {
} elseif ($fou == 'haz') {
}

像这样浓缩,你可以很容易地看到绊倒(无论是打字错误,还是诚实的区别)。但是有了一个开关,你就明白了这意味着什么:

1
2
3
4
5
6
7
8
9
10
switch ($foo) {
    case 'bar':
        break;
    case 'baz':
        break;
    case 'buz':
        break;
    case 'haz':
        break;
}

另外,更容易阅读:

1
2
if ($foo == 'bar' || $foo == 'baz' || $foo == 'bat' || $foo == 'buz') {
}

1
2
3
4
5
case 'bar':
case 'baz':
case 'bat':
case 'buz':
    break;

从性能角度来看…好吧,别担心表演。除非你在一个紧密的循环中做了几千次,否则你甚至无法分辨出两者之间的差别(差别可能在微秒范围内,如果不是更低的话)。

找到最易读的方法。这是最重要的部分。不要尝试微观优化。记住,Premature Optimization Is The Root Of All Evil


您应该考虑使用多态性作为控制结构(如if或switch)的替代方法。如果您有很多选项,这就特别方便了,因为它可以大大简化控制逻辑。

我在这里写过这个概念:http://codeutopia.net/blog/2009/02/02/avoing-unless-switch-case-structures-with-classes/


switch告诉后来的读者,您是基于单个值的值进行分支的,否则他们必须通过查看所有条件来暗示这一点。为了清楚起见,我更喜欢这个开关