zoukankan      html  css  js  c++  java
  • 写出优雅简明代码的论题集 Csharp(C#)篇[转]

    最近和一些朋友讨论如何写出优雅的代码,我们都很喜欢C#,所以以C#为例。主要一共有三位程序员在一起讨论,为简单起见我用ABC代表我们三个人。

    有时候我们会针对一些代码进行讨论,有时候我们会提出一些观点,有时候我们会一起学习网上一些现有的博客,为了便于大家引用,我给每一个论题都编上号。

    在很多情况下,我们的意见统一,那么我会给大家呈现我们的结论;但是有些情况我们有分歧。

    你可以加入我们的讨论,我非常也希望能够获知你的意见,让我们一起茁壮成长!

    好吧,让我们今天就开始。

    论题一:函数越小越好!

    象鼠

    相信绝大部分程序员会认同这一点,维护一个超过100行的函数会让人抓狂。

    我记得我以前修改过一个用cobol写的程序,一个文件超过10万行,我为了进行一个极其小的修改花了3天的时间,而且最后自己也不知道会不会造成什么严重的后果。-- 这已经过去8年了,希望那段代码运行良好。

    到底理想状态下,我们的函数应该不大于多少行?我们三个人的答案是:

    A: 10 行

    B: 15 行

    C: 20 行

    论题二:用 Linq 简化代码

    Linq有时可以帮助我们写出一些非常“人性”的语句。

    下面的这个函数是用于在数据库中插入新的评论:

            public static void Create(IEnumerable<CommentData> Comments, SqlConnection cn)
            {
                // validate params
                if (null == cn) throw new ArgumentNullException("cn");
                if (cn.State != ConnectionState.Open) throw new ArgumentException("Invalid parameter: connection is not open.", "cn");
                if (null == Comments) throw new ArgumentNullException("Comments");
                foreach (CommentData data in Comments)
                {
                    if (data.CommentId.HasValue)
                        throw new ArgumentNullException("Create is only for saving new data.  Call save for existing data.", "data");
                }
    ....

    其中foreach这一部分可以简化为

                if (Comments.Any(data => data.CommentId.HasValue))
                {
                    throw new ArgumentNullException("Create is only for saving new data.  Call save for existing data.", "data");
                }
    


    在这一点上,我们存在分歧,A认为没有必要进行简化,因为原来的已经很明确了;但B认为简化后的代码可读性更强,看上去更加直接。

    希望每个人都已经知道C#的这个用法了,直接上一些代码:

    3.1

    原始代码:

    List<int> idsToFind = new List<int>();
    idsToFind.Add(1);
    idsToFind.Add(2);

    修改后:

    List<int> idsToFind = new List<int> {1, 2};

    3.2

    原始代码:

    var startingPoint = new Point();
    startingPoint.X = 5;
    startingPoint.Y = 13;
    修改后:
    var startingPoint = new Point() { X = 5, Y = 13 };

    论题四:运用 ?:和??

    据说,有些公司会拿这个来测试入门的程序员:

    4.1

    原始代码:

    if (c != null)
        System.Console.WriteLine(c.Name);
    else
        System.Console.WriteLine("List element has null value.");

    修改后:

    System.Console.WriteLine(c != null ? c.Name : "List element has null value.");

    4.2

    原始代码:

    string name = value;   
    if (value == null)
    {
    name = string.Empty;
    }

    修改后:
      string name = (value != null) ? value : string.Empty;
    还可以更简单,变成:
    string name = value ?? string.Empty;

    论题五: 运用AS

    原始代码:

    if (employee is SalariedEmployee)
    
    {
        var salEmp = (SalariedEmployee)employee;
        pay = salEmp.WeeklySalary;
        // ...
    }

    修改后:

    var salEmployee = employee as SalariedEmployee;
    if (salEmployee != null)
    {
       pay = salEmployee.WeeklySalary;
        // ...
    }

    论题六: 运用 using

    using首次出现是在visual studio 2005 中,在这以前,很多程序员晕倒在了释放资源的逻辑中。

    使用using语句实际上生成的IL代码中是一个try, finally代码块,在finally代码块里释放资源。

    原始代码:
    public IEnumerable<Order> GetOrders()
    
    {
        var orders = new List<Order>(); 
        var con = new SqlConnection("some connection string");
        var cmd = new SqlCommand("select * from orders", con);
        var rs = cmd.ExecuteReader(); 
        while (rs.Read())
    
        {
    
            // ...
    
        } 
        rs.Dispose();
        cmd.Dispose();
        con.Dispose(); 
        return orders;
    } 

    这是一段非常丑陋的代码,我们完全迷失在dispose群中,什么时候要调用哪个dispose啊? 天哪? 如果我们用 finally, 可以将代码写为:

           

    public IEnumerable<Order> GetOrders()

    {

        SqlConnection con = null;

        SqlCommand cmd = null;

        SqlDataReader rs = null;

        var orders = new List<Order>();

        try

        {

            con = new SqlConnection("some connection string");

            cmd = new SqlCommand("select * from orders", con);

            rs = cmd.ExecuteReader();

            while (rs.Read())

            {

                // ...

            }

        }

        finally

        {

            rs.Dispose();

            cmd.Dispose();

            con.Dispose();

        }

        return orders;

    }

    看看using到底给我们带来了什么:

     
    public IEnumerable<Order> GetOrders()
    
    {
    
        var orders = new List<Order>(); 
       using (var con = new SqlConnection("some connection string"))
        {
            using (var cmd = new SqlCommand("select * from orders", con))
            {
                using (var rs = cmd.ExecuteReader())
                {
                    while (rs.Read())
                    {
    
                        // ...
    
                    }
    
                }
    
            }
    
        } 
        return orders;
    } 
    好多了,对吗? 完全不用再用那一堆的try/finally 代码了,也不用使用一堆的null,为了使代码更轻巧,让我们再做小小修改:
     
          public IEnumerable<Order> GetOrders()
    {
    
        var orders = new List<Order>(); 
    
        using (var con = new SqlConnection("some connection string"))
        using (var cmd = new SqlCommand("select * from orders", con))
        using (var rs = cmd.ExecuteReader())
        {
            while (rs.Read())
            {
               // ...
            }
        } 
        return orders;
    
    } 


    对程序员而言,我们的代码需要:

    1. 在预算内实现需求,让用户可以使用 -- 让自己或者公司可以赚到钱

    2. 方便自己修改及日后维护

    3. 方便别人修改及日后维护

    4. 便于重复使用,为以后的开发节省时间

    5. 让系统高效的运作

    从美国商学院毕业的学生们掌握了很多相似的思维模式,这不仅有利于他们解决问题,更重要的是方便他们彼此之间沟通。-- 换句话说,他们毕业后都安装上了相同的协议和一些可通用的接口,这样有一个基础平台可以让他们协同工作。

    论题七:命名规范

    也许有人认为没有必要再提这个问题,但在日常编码生活中,这的确是一个很重要的话题。

    7.1  类名、方法、常数使用Pascal casing

    public class MyClass
    {
        const int DefaultNumber = 100;
        public void MyMethod()
        { }
    }

    7.2 局部变量,参数用camel casing

     

                partial void OnContactIdChanging(int value)
                {
                    int number;
    
                }
    

     

    7.3 interface 名字以I 开头

    7.4 尽量不用单个字符命名变量,象 i 或者 t 。使用 index 或者 temp 之类代替。

    7.5 将所有来自framework 的 namespace 放在前面,而后再放第三方或自定义的: 

    using System;
    using System.Linq;
    using System.Data.Linq;
    using System.Collections.Generic;
    using System.Text;
    using System.ComponentModel.DataAnnotations;
    using CodeSmith.Data.Attributes;
    using CodeSmith.Data.Rules;
    
     

    论题八: 一个方法的参数不能超过5个,当多于5个时,应进行函数的拆分或者参数的封装。-- 嚯嚯就像论题一样的规定

    一些说明:不是为了给自己一个紧箍咒,而是在日常编程中,我们发现如果你写的方法不满足这样一个条件,一年后,就算是你自己也不太想去维护和修改,如果换成是其他程序员会对此更加的头痛,对吗?

    论题九: 不要滥用注释,有些非常清晰明确的代码不需要注释

    仅在必要的时候注释你的代码,不要太多,并且注释也要简单给力。

    论题十: 不要把数值hard-code在代码中,使用const 来定义

    论题十一: 不要使用””, 使用string.Empty

    正确的:

    string name = string.Empty;

    不建议:

    string name = "";

    论题十二: 善于合并if

    观察下面这段可爱的代码:


    public bool Equals(CommentData obj) {
    if (!CommentId.Equals(obj.CommentId)) return false;
    if (!Comment.Equals(obj.Comment)) return false;
    if (!CommentorId.Equals(obj.CommentorId)) return false;
    return true;
    }
    public bool Equals(CommentData obj) {
    if (!CommentId.Equals(obj.CommentId)) return false;
    if (!Comment.Equals(obj.Comment)) return false;
    if (!CommentorId.Equals(obj.CommentorId)) return false;
    return true;
    }

    如果我们写成这样会不会好些呢:

    public bool Equals(CommentData obj) {
    return CommentId == obj.CommentId &&
    Comment.Equals(obj.Comment)
    &&
    CommentorId
    == obj.CommentorId;
    }
    public bool Equals(CommentData obj) {
    return CommentId == obj.CommentId &&
    Comment.Equals(obj.Comment)
    &&
    CommentorId
    == obj.CommentorId;
    }

    论题十三: 不断重构你的代码

    当有新的需求或新改动的时候,可以拨一些时间来重构。 -- 你可能突然发现,原来重构后的代码可以如此美丽。使用一些重构的插件,比如resharper可以使你事半功倍。

    未完待继
  • 相关阅读:
    怎么获取pm2.5数据----pm2.5 的获取 java 通过url获取后,得到json 格式,在解析json
    MVC介绍
    如何通过URL获取天气PM2.5的数据,返回json
    23种设计模式
    xxx系统的6大质量属性战术
    作业04.提升系统性能
    淘宝网的质量属性分析
    架构漫谈读后感
    软件架构师工作流程----装修与软件的联系
    软件构架实践阅读笔记五(读后感)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/1963761.html
Copyright © 2011-2022 走看看