继续说一说昨天提到的那个代码重构

    昨天我给了一段.NET Framework 2.0中的一段代码,希望大家能重构一下。结果大家没有对代码本身提出什么积极的看法,倒是有几位热心人代码都没有看清,就大肆予以评论(因为那段代码是错的,现已修正)。下面再说一下这段代码的特点和问题,欢迎有兴趣并能看到诗的博友斧正。

    还是说一下代码的来源,这段代码确实是使用Reflector反编译出来的,不过从代码的优化痕迹来看,是比较忠实于最初的源代码的。另一个反证是新的源代码编译后再反编译,结果仍是很相近。上次给出的那段代码,一眼看上去确实是挺让人郁闷的,正如robaggio同学搞笑的说:也许是某种inline机制吧。。。 

    而仔细看昨天那段代码,除了行数多外,从效率上来说基本已是无懈可击。这么说有什么依据,凭什么说这个代码很优化?那么我们来看一下.NET Framework 1.1中QuoteJScriptString(string)方法的实现:
public static string QuoteJScriptString(string value)
{
    StringBuilder strbQuoted 
= new StringBuilder(value.Length + 5);
    
for ( int i = 0; i < value.Length; i++)
    
{
        
char ch = value[i];
        
if (ch <= '"')
        
{
            
switch(ch)
            
{
                
case '\t':
                
{
                    strbQuoted.Append(
@"\t");
                    
continue;
                }

                
case '\n':
                
{
                    strbQuoted.Append(
@"\n");
                    
continue;
                }

                
case '\v':
                
case '\f':
                
{
                    strbQuoted.Append(value[i]);
                    
continue;
                }

                
case '\r':
                
{
                    strbQuoted.Append(
@"\r");
                    
continue;
                }

                
case '"':
                
{
                    strbQuoted.Append(
"\\\"");
                    continue;
                }

            }

        }

        
else
        
{
            
if (ch == '\'')
            {
                strbQuoted.Append(
@"\'");
                
continue;
            }

            
if (ch == '\\')
            
{
                strbQuoted.Append(
@"\\");
                
continue;
            }

        }

        strbQuoted.Append(value[i]);
    }

    
return strbQuoted.ToString();
}

    要想知道自己写的代码是否是业余水平,我认为可以参考一下上面这段代码。这段代码我觉得唯一有意义的地方就是创建StringBuilder对象的时候,给出了Capacity的大小,而且也仅此这一点显得和业余水平之间有了一点点的差距。至于为什么判断ch <= '"',我没有太明白,是因为swith中case太多会有什么效率问题吗?虽然'"'是ASCII中顺序里的第二个可打印字符(不算Space),但是这样将ch的检测路径分开,我没有觉得有什么大的意义。

    那么2.0中的QuoteJScriptString有什么特点呢?一是:修改了bug,给QuoteJScriptString添加了一个forUrl的bool参数,指示转换时是否转换"%"为其escape(JScript中的全局函数)形式。因为如果当你把脚本代码使用到link的href中时,IE会自动的unescape这个字符串,再作为代码解析执行。看下面的示例:
<href="JavaScript: alert('%25%');">click</a>

    如果在IE中你点击了click后,你觉得MsgBox里会显示啥?是"%25%"吗?实际上显示的是:"%%"。这就是为什么新版的方法要添加forUrl开关参数的原因,是为了修复bug。二是:优化了QuoteJScriptString方法的效率,除了继续沿用了创建StringBuilder那个地方外,还有三点。1) 做了参数合法性检查;2) 消除了不必要的StringBuilder实例的创建;3) 对于不用替换的字符,使用区段处理代替了逐字处理。

    既然都说昨天那段挺不错了,那么还重构个头啊?其实重构只是一个镢头了。至于考证代码是不是微软原版的,我只能说:别人看到了诗,有的人看到屎。真的要重构,也就主要是节省点代码了,我相信程序员都是很懒的,即使打字再快,毕竟我们也不是打字员哈。给一个重构后的版本,代码如下:
public static string QuoteJScript(string value, bool forUrl)
{
    
if (StringHelper.IsEmpty(value))
    
{
        
return string.Empty;
    }

    StringBuilder strbQuoted 
= null;
    
int i, position;
    i 
= position = 0;
    const string QUOTED_CHARS= "\t\n\v\f\r\"%'\\";
    string  [] QUOTED_STRINGS = 
        
@"\t"@"\n"@"\v"@"\f"@"\r"@"\"""@"%25"@"\'"@"\\" };
    
for ( ; i < value.Length; i++)
    
{
        
char ch = value[i];
        
int index = QUOTED_CHARS.IndexOf(ch);
        
if ( index >= 0 )
        
{
            
if ( ch != '%' || ( ch == '%' && forUrl ) )
            
{
                
if ( strbQuoted == null )
                
{
                    strbQuoted 
= new StringBuilder(value.Length + 5);
                }

                
if ( i-position >= 1 )
                
{
                    strbQuoted.Append(value, position, i
-position);
                }

                strbQuoted.Append(QUOTED_STRINGS[index]);
                position 
= i + 1;
            }

        }

    }

    
if (strbQuoted == null)
    
{
        
return value;
    }

    
if ( i-position >= 1 )
    
{
        strbQuoted.Append(value, position, i
-position);
    }

    
return strbQuoted.ToString();
}

    最后有一个有趣的问题,你认为Fx 1.1和2.0中两个QuoteJScriptString是同一个人写的吗?你觉得这种重构方式好不好?是你会怎么做呢?

posted on 2005-07-16 00:51  birdshome  阅读(4444)  评论(24编辑  收藏  举报

导航