天天看點

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>

<%}

<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>

<%} %>

變成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>

@[email protected]<br />

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>

<ol id = "@Model.DropableID("SAME")" class = "dragable">

@foreach (var view in SFCView.SameViews())

<ol id = "@Model.DropableID("NEXT")" class = "dragable">

@foreach (var view in SFCView.NextViews())

解決方法是使用Model傳入三個IEnumerable:

<ol id = "@Model.DroppableID(SFCView.PREV)" class = "dragable">

@{Html.RenderPartial("_ViewList", Model.RelatedViews(SFCView.PREV));}

<ol id = "@Model.DroppableID(SFCView.SAME)" class = "dragable">

@{Html.RenderPartial("_ViewList", Model.RelatedViews(SFCView.SAME));}

<ol id = "@Model.DroppableID(SFCView.NEXT)" class = "dragable">

@{Html.RenderPartial("_ViewList", Model.RelatedViews(SFCView.NEXT));}

這裡就固定三種情況,基本上這樣可讀性和可維護性就可以了。

如果重複的次數更多,就要考慮把td一起搬進一個更大的循環體中。

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

大緻意思就是不要寫:

if (...)

A();

B();

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