C#编码简单性之代码篇(如何编写简短的C#代码,随时更新)

以前写C++的时候曾经在自己网站上发表过一个编码“简单性”之文章,现在编写C#了才发现自己无意之间就会写下一些浪费屏幕的代码。

下面是自己编码中偶然发现的一些案例,欢迎中等水平的编程者参考。因为要积累案例,所以随时更新。


编码简单性的“心法”就是:只要屏幕上有任何两部分代码看上去相似,则一定有合并办法。

无论在微观还是宏观层面上这一点都适合。在02年的时候,我们曾在2小时内把一个程序员的4000多行的65个函数变为一个函数,相当于一个月的工作量被取代;04年则令人发指地发生了1个人用1.5年重新编写了13个人编写了9年的程序的事件。

因此,应随时关注代码中的“不简洁”现象,一旦放任其发生,软件将很难维护。


案例1 合并相似代码

以前遇到过的最极端的例子是:

 

switch (n)

{

case 1: return 1;

case 2: return 2;

case 3: return 3;

case 4: return 4;

case 5: return 5;

case 6: return 6;

default: return n;

}

这段代码其实相当于:return n;

这可不是个笑话,是01年一位还不错的同事编写的,原封未动就是这个样子6个整数。当我们指出来的时候,她忍不住笑了……人就这样,总有走火入魔的时候。

2011-07-31:今天不小心自己写的一段代码:

if (!result.Contains("true"))
{
_repSFC.GrantAuthorityToRole(authority, role, false);
}
if (result.Contains("true"))
{
_repSFC.GrantAuthorityToRole(authority, role, true);
}
其实就是:

_repSFC.GrantAuthorityToRole(authority, role, result.Contains("true"));
 

要发现它们,只需要牢记心法:只要屏幕上有任何两部分代码看上去相似,则一定有合并办法。

 

 


 

 

 

案例1.5 多用?+:语法

另一个小案例:

if (Misc == null)
return SFCCatches.LinkP2Cs.Where(i => i.P == p && i.C == c);
else
return SFCCatches.LinkP2Cs.Where(i => i.P == p && i.C == c && i.Misc == Misc);
改为:

return SFCCatches.LinkP2Cs.Where(i => i.P == p && i.C == c && (Misc == null ? true : i.Misc == Misc));
有时候感觉这种写法有点花哨,但是习惯以后,实际可读性要高得多,尤其如果单行代码挺长的时候。


案例2 推迟分支(请先看案例4)

这个长一些,原来的代码是aspx,在重构成Razor的时候发现可以简化。

<% foreach ( var techDebt in Model.TechDebts) 
{ %>
<% if (techDebt.IsOpen == true) 
{ %>
<div class = "Black">
<a href="/Agile/TechDebts/Edit/<%= techDebt.TechDebtID %>" target = "_blank" ><img title = "编辑" alt = "编辑" class = "icons" src="../../../../Resouces/Images/XXX/TechDebts/TechDebtOpening.png" /></a>
<%= techDebt.Type %>-<%= techDebt.Title %><br />
</div>
<%}
else
{ %>
<div class = "Gray">
<a href="/Agile/TechDebts/Edit/<%= techDebt.TechDebtID %>" target = "_blank" ><img title = "编辑" alt = "编辑" class = "icons" src="../../../../Resouces/Images/XXX/TechDebts/TechDebtsClosed.png" /></a>
<%= techDebt.Type %>-<%= techDebt.Title %><br />
</div>
<%} %>
<%} %>
变成Razor后等同于:

@foreach ( var techDebt in Model.TechDebts) 

string color = techDebt.IsOpen ? "Black" : "Gray"; //也可以放到下面的@color那里,但不太直观。
string img = techDebt.IsOpen ? "TechDebtOpening.png" : "TechDebtsClosed.png"; //同上。
<div class = @color>
<a href="/Agile/TechDebts/Edit/@techDebt.TechDebtID" target = "_blank" ><img title = "编辑" alt = "编辑" class = "icons" src="../../../../Resouces/Images/XXX/TechDebts/@img" /></a>
@techDebt.Type-@techDebt.Title<br />
</div>
}

19行代码变成9行。虽然看起来不起眼,但同比例放大10000倍看:把9万行代码编写成19万行,绝对是一个灾难。

同时注意color/img的处理,因为若把他们嵌入下面的代码中,代码会显得不直观,所以无需处理。毕竟看似多了2行“无用代码”,但他们与别处并无重复,理解起来也更简单。因此简单性是信息的简单性/无重复性,而不是简单地删除分号。


案例3

下面一段代码中,三个TD中的三个函数PrevViews/SameViews/NextViews破坏了简单的合并:

<td>
<ol id = "@Model.DropableID("PREV")" class = "dragable">
@foreach (var view in Model.PrevViews())
{
<li class = "black" id = "@view.DragableID()">
@Html.ImageLink("../../Resouces/Images/" + @view.Area + "/" + @view.Controller + "/" + @view.Action + "16.png",
view.Title, "imagelink", false, view.Action, view.Controller, view.Area, new { }, new { }, new { })
@(view.Title + "(" + view.Area + "/" + view.Controller + "/" + view.Action + ")")<br />
</li>
}
<br />
</ol>
</td>
<td>
<ol id = "@Model.DropableID("SAME")" class = "dragable">
@foreach (var view in SFCView.SameViews())
{
<li class = "black" id = "@view.DragableID()">
@Html.ImageLink("../../Resouces/Images/" + @view.Area + "/" + @view.Controller + "/" + @view.Action + "16.png",
view.Title, "imagelink", false, view.Action, view.Controller, view.Area, new { }, new { }, new { })
@(view.Title + "(" + view.Area + "/" + view.Controller + "/" + view.Action + ")")<br />
</li>
}
<br />
</ol>
</td>
<td>
<ol id = "@Model.DropableID("NEXT")" class = "dragable">
@foreach (var view in SFCView.NextViews())
{
<li class = "black" id = "@view.DragableID()">
@Html.ImageLink("../../Resouces/Images/" + @view.Area + "/" + @view.Controller + "/" + @view.Action + "16.png",
view.Title, "imagelink", false, view.Action, view.Controller, view.Area, new { }, new { }, new { })
@(view.Title + "(" + view.Area + "/" + view.Controller + "/" + view.Action + ")")<br />
</li>
}
<br />
</ol>
</td>
解决方法是使用Model传入三个IEnumerable:

<td>
<ol id = "@Model.DroppableID(SFCView.PREV)" class = "dragable">
@{Html.RenderPartial("_ViewList", Model.RelatedViews(SFCView.PREV));}
<br />
</ol>
</td>
<td>
<ol id = "@Model.DroppableID(SFCView.SAME)" class = "dragable">
@{Html.RenderPartial("_ViewList", Model.RelatedViews(SFCView.SAME));}
<br />
</ol>
</td>
<td>
<ol id = "@Model.DroppableID(SFCView.NEXT)" class = "dragable">
@{Html.RenderPartial("_ViewList", Model.RelatedViews(SFCView.NEXT));}
<br />
</ol>
</td>
这里就固定三种情况,基本上这样可读性和可维护性就可以了。

如果重复的次数更多,就要考虑把td一起搬进一个更大的循环体中。


案例4 2011-05-05:推迟分支

大致意思就是不要写:

if (...)

{

A();

B();

}

else

{

A();

C();

}

而是要写

A();

if (...)

{

B();

}

else

{

C();

}

心法是:任何两个地方看上去相似,就可以简化。技法是:相同部分放在分支前或后,不同部分才是分支。

这个案例看起来是愚蠢的,因为谁能看不出来呢,但实际结果不然,比如下面这段今天刚要写的代码(在一个url后面加上新的参数zoom=1):

url = (uri.Query.Count() == 0) ? uri.PathAndQuery + "?zoom=" + level : uri.PathAndQuery + "&zoom=" + level;

其实应该是:

uri.PathAndQuery + (uri.Query.Count() == 0 ? "?" : "&") + "zoom=" + level;

更隐蔽的是案例2。


本文转自火星人陈勇 51CTO博客,原文链接:http://blog.51cto.com/cheny/1100081


上一篇:创业18般武器之ICP许可证过审秘籍


下一篇:分区表建立索引