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;

其实应该是:

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

更隐蔽的是案例2。

 

点击下载免费的敏捷开发教材:《火星人敏捷开发手册

 

posted @ 2011-04-02 20:28  我的一天  阅读(253)  评论(0编辑  收藏  举报