以前LZ也是一个满篇注释的Coder,但是自从到这个公司,除了写别人调用的api,
有个简单的api功能描述,其他什么都没有。OK,我就不光说不练了,给大家看看我的代码先。
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Collections.Specialized;
using System.Web;
using System.IO;
using XXX.Lib;namespace XXX
{
    // Url辅助类:对Url进行初步的解析
    public class UrlHelper
    {
        const int MAX_URI_LENGTH = 512;
        string _scriptName = string.Empty;
        CommandResult _parseResult = CommandResult.Success;
        NameValueCollection _parameters = new NameValueCollection();
        char[] _uriInvalidChar = new char[] { '/', '\\' };
        char[] _pathInvalidChar = new char[] { '/', '\\', ':', '*', '?', '\"', '<', '>', '|' };
        public Uri _uri = null;        public string ScriptName
        {
            get { return _scriptName; }
        }        public NameValueCollection Parameters
        {
            get { return _parameters; }
        }        public CommandResult ParseResult
        {
            get { return _parseResult; }
        }        public UrlHelper(Uri originalUri)
        {
            _uri = ReplaceUri(originalUri);            if (IsUriLengthError())
            {
                return;
            }            if (CheckPathAndQuery())
            {
                ParsePathAndQuery();
            }        }        private Uri ReplaceUri(Uri originalUri)
        {
            string uriStr = ReplaceUriWithUrlModel(originalUri);            if (uriStr == "")
            {
                return originalUri;
            }
            else
            {
                return new Uri(uriStr);
            }
        }        private static string ReplaceUriWithUrlModel(Uri originalUri)
        {
            string[] splitStr = null;
            try
            {
                foreach (var item in File.ReadAllLines(Hisan.Lib.SystemLib.GetAppRootDir() + "UrlModel.ini"))
                {
                    splitStr = item.Split(new string[] { "->" }, StringSplitOptions.RemoveEmptyEntries);
                    if (splitStr == null || splitStr.Length != 2)
                    {
                        continue;
                    }                    if (splitStr[0].Trim() == originalUri.PathAndQuery.Substring(1))
                    {
                        return originalUri.AbsoluteUri.Replace(splitStr[0].Trim(), splitStr[1].Trim());
                    }
                }
            }
            catch
            {            }
            return "";
        }        private bool IsUriLengthError()
        {
            if (_uri == null || _uri.ToString().Length > MAX_URI_LENGTH)
            {
                _parseResult = CommandResult.UrlTooLong;
                return true;
            }
            return false;
        }        private bool CheckPathAndQuery()
        {
            string pathAndQuery = _uri.PathAndQuery.Substring(1);            if (IsUrlInvalidChar(pathAndQuery))
            {
                return false;
            }            if (pathAndQuery.IndexOfAny(_uriInvalidChar) >= 0)
            {
                _parseResult = CommandResult.UrlInvalidChar;
                return false;
            }
            else if (pathAndQuery.Length == 0)
            {
                _parseResult = CommandResult.NoExistsMethod;
                return false;
            }            string[] splitPathAndQuery = new string[] { };
            if (IsFileNameInvalidChar(pathAndQuery, splitPathAndQuery))
            {
                return false;
            }            return true;        }        private bool IsFileNameInvalidChar(string pathAndQuery, string[] splitPathAndQuery)
        {
            splitPathAndQuery = pathAndQuery.Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries);
            if (splitPathAndQuery[0].IndexOfAny(_pathInvalidChar) >= 0)
            {
                _parseResult = CommandResult.FileNameInvalidChar;
                return true;
            }
            return false;
        }        private bool IsUrlInvalidChar(string pathAndQuery)
        {
            if (pathAndQuery.IndexOfAny(_uriInvalidChar) >= 0)
            {
                _parseResult = CommandResult.UrlInvalidChar;
                return true;
            }
            return false;
        }        private void ParsePathAndQuery()
        {
            string[] splitPathAndQuery = _uri.PathAndQuery.Substring(1).Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries);
            SetScriptNameAndParameters(splitPathAndQuery);
        }        private void SetScriptNameAndParameters(string[] splitPathAndQuery)
        {
            _scriptName = splitPathAndQuery[0];            if (splitPathAndQuery.Length > 1)
            {
                _parameters = HttpUtility.ParseQueryString(splitPathAndQuery[1], Encoding.UTF8);
            }
        }    }
}
有人会问,为什么写成这么多小方法,其实这个是来源于一本书 CleanCode,而不写注释的想法也是来源于这本书。
文章中心思想是为了以写故事的方式来写程序。拿出一小段为例,我给加上中文注释看看效果如何。        public UrlHelper(Uri originalUri)
        {
           //替换Uri,如果跟进这个方法看的话,会发现把原来的请求更换成已经内置好的Url,这个是为了兼容旧版本Url的,当然这个本不应该存在,但当时那个版本没有自动升级,所以这个没办法。权且当成合适的好了,而且这也不是本次讨论的重点。
            _uri = ReplaceUri(originalUri);
            //如果Uri的长度错误
            if (IsUriLengthError())
            {
                //返回
                return;
            }
            //如果检查路径和查询成功(路径和查询这个暂且这么叫,这个做过Web的都知道Uri.PathAndQuery是获得host后面的字符串的。
            if (CheckPathAndQuery())
            {
                //解析路径和查询
                ParsePathAndQuery();
            }        }
其实写程序也无非这样,根据汉字的意思就可以知道构造函数到底做了什么,想做什么。
不写注释的原因是一些简单的单词对于程序员来说是绝对看的懂的。
我们的英文虽然不怎么样,但说实话,就算写成中国版的英文又如何?他只能笑你不会英文,但不会笑你程序写的不好。
而且我相信这个大家都看的懂(在公司没有老外的情况下,否则真应该规范下英语了)
另外一个原因是维护的时候,或者修改Bug的时候大多数人都会忘了去修改注释,导致注释和程序不匹配。
这样后来的人看注释和程序不匹配,他就会想到底哪个错了。就算他不想,只认为程序是对的,OK。
那他还要想改不改错误的注释呢?虽然这个是不应该出现的问题,但这种问题却常常出现。当然这种方法利弊都有,下面说下弊端。
1.对开发工具要求较高,当然VS还是很给力的,F12就可以跟着方法名进入方法。
但一些没有此功能的开发工具来说,去那么多代码中来找对应的方法,还是很头疼的。
2.较大的项目不太适合,比如一些开源的。那么多类,还全是英文,没有注释,找起来着实头疼。另外一般那样的项目修改的人很多,出现几个不受规矩的程序员就着实让人头疼,他看着别人不写注释自己也不写,但是逻辑组织的又不是很好。本文仅作抛砖引玉之用,看看大家对无注释编程有何看法,以及普遍程度如何。
但就目前来看,大多数人看到没有注释的程序都会大喊“垃圾”。尤其篇幅较长的。但有些篇幅较长的确实是因为其特殊性没有办法进行小方法拆分,比如一些特殊的算法,拆开了反而逻辑不好。

解决方案 »

  1.   


    我的函数名 有的用汉语拼音,有的用英语,看着好恶心好恶心。
    比如 User Beep
    比如 yonghu ICka 
    我觉得对于我这种菜逼  加上_下划线 好看一点
    例如 yonghu_ID  哈
      

  2.   

    yonghu ICka
    可以用 UserICCard,而且这种变量名一般是采用 匈牙利命名法的
    其实多写几次就好了,熟能生巧。
      

  3.   

    除非记忆力超群,否则自己写的代码,放到2/3年后,即使自己看也头大.适量注释还是很不错的.书写code时候也是一个思路的整理过程
      

  4.   

    有时编程还是会省掉。
    不过回头看到没有注释的程序时还是会不自觉的加上的。建议下个stylecop软件试试。另外程序注释有了,生成相关的文档就方便了。
      

  5.   

    我建议还是最好加上注释   假如你学c++ 但是老板突然叫你学c#  1年后在叫你去搞c++ 肯定有点陌生
    那么你拿注释的代码来一看 就了解了
    有注释的代码更便于维护升级
      

  6.   

    LZ这种函数调用式的编码习惯值得鼓励,但不得不说LZ对注释的理解有点偏差,拿LZ帖子中的例子来说://如果Uri的长度错误
    //返回
    //如果检查路径和查询成功这种注释无论代码风格如何,都属于废话注释,完全可以删掉真正的注释应该出现在变量命名、类方法、复杂流程逻辑、多层分支等等这些位置,而这些和LZ的CleanCode并无关系
      

  7.   

    如果你打算使用所谓“无注释编程”,确保你同时有完备的单元测试。否则,为代码的接口加上必要的文档是必不可少的。比如,你写了一个int IndexOfString(string source, string FindFor)函数,现在你的同事觉得效率比较差,要改写了,的确,看字面意思也差不多。但是鬼才知道,如果FindFor传空字符串,返回什么?如果source为空,返回什么?如果找不到,返回什么?返回的位置,是从0开始的索引(-1表示没找到),还是1开始的(0表示没找到)。也许你的同事不懂什么是单元测试,也懒得看你的代码,他会写一个程序,把以上边界条件代入测试一下,然后改写完成再测试一下,保持一致性。事实上,他这个过程,就是给遗留代码建立单元测试的过程。
      

  8.   


    这个好像你没仔细看我的主题哦,我说了,方法名要有意义!
    一个方法内就要像一个故事一样,把自己做的事来龙去脉写清楚。这样就不怕自己回头看不懂了嘛。
    建议大家看看Clean Code这本书,很好的。他的思想比较前卫。
      

  9.   


    你说的一般是接口,接口肯定要规范的。
    这个东西确实不好说,萝卜青菜各有所爱嘛,Clean Code还是有其可取之处的。他崇尚的就是代码即文档。这个大家看法不同而已。
      

  10.   


    因人而异,我说的无注释所说的该写的地方仅限于特殊返回值,接口等这些地方。而强调必须写注释的人是几乎遍布整篇代码的注释。稍微好点是强制要求每个方法都要有。
    而这个在Clean Code所提倡的一个函数只做一件事,以故事描述代码里是不现实的。尤其标准的方法就算没有参数也要三行,带参数就更不用说了。
    另外维护的时候忘记修改代码忘记更新注释的话同样会让人费解。无注释编程注重的是非特殊情况不加注释(不是一点不加)。
      

  11.   

    看来大多数人还是喜欢注释的。
    但我身边做过10几年开发的人,他们的代码都是没有注释的。
    更有用OpenGL做算法的,里边充斥着0.1,0.2类似的代码,都没有注释的,着实让人头疼。
    必要的地方为了说明原因确实需要有注释。否则真的就像LS几位说的。
    过几年回过头看,自己都不知道写了什么。
    但是大多数地方只要命名得当,没有注释也是一样的。
      

  12.   

    呵呵,居然被推荐了,感谢感谢。
    好多人回复啊。我就不一一回了。大多数人对无注释都表示不理解,都认为无注释害人害己。
    但其实,无注释是有要求的。并不适合新人。
    作为一些混迹Code界数载的人来说,无注释其实并无不可。首先我再举个例子,虽然不符合正常编程逻辑,但一时手头没有符合这种情况的。
    大家讲究下能明白意思就可以。就是获取当前字符串所对应的Index。假设int>=0,如果没有返回-1。
    那么如果别人调用这个方法,返回值是-1,他是想就有-1这个Index呢还是异常呢?
    又或者调用的人在做边界限制的时候,他以什么为标准来判断GetIndex出错了呢?
    这个时候没有注释,着实坑爹了。总不会要人家来你方法里看看出错的返回值吧。
    相信用习惯VS的人都比较依赖 ".",我个人也很依赖。public int GetIndex(string str)
    {
       Dictionary<string,int> d = new Dictionary<string,int>();
       d.Add("aa",1);
       try
       {
            return d[str];
       }
       catch
       {
           return -1;
       }
    }那我为什么又要提出无注释这种想法。
    就如我主楼所写一样。
    如果变量名、函数名、函数体逻辑都能够表达你所要做的事情。
    那我觉得写注释反而累赘。
    当然这样的代码不适合新人来看的。
    我确实见过Dictionary都不知道的人混迹在这行里。
    但是我相信,任何Coder都要学会看英文,因为我们的开发就是基于英文的。
    我们可以写一些简单的英文单词,虽然是中式英文,好歹以我们汉语的语法习惯是读得懂的。比如一个简单的小例子        /// <summary>
            /// 设置参数
            /// </summary>
            /// <param name="parameterName">参数名</param>
            /// <param name="value">参数值</param>
            public void SetParameter(string parameterName, string value)
            {
                Dictionary<string, string> d = new Dictionary<string, string>();
                if (d.ContainsKey(parameterName))
                {
                    d[parameterName] = value;
                }
                else
                {
                    d.Add(parameterName, value);
                }
            }
    SetParameter已经很明显的表达这个函数是要设置参数的。
    parameterName -- 参数名
    value -- 参数值这些都是显而易见的,又为何要多此一举的加上他呢?当然这个例子很简单,也有复杂很多的。
    但是遵循一个函数只做一件事的原则。
    只要函数名得当,变量名得当。
    不需要注释一样看的懂。论坛签名======================================================================dodducs:你好!
    截至 2011-10-31 10:22:05 前:
    你已发帖 23 个, 未结贴 0 个;
    结贴率为: 100.00%

    当您的问题得到解答后请及时结贴.

    http://topic.csdn.net/u/20090501/15/7548d251-aec2-4975-a9bf-ca09a5551ba5.html
    http://topic.csdn.net/u/20100428/09/BC9E0908-F250-42A6-8765-B50A82FE186A.html
    http://topic.csdn.net/u/20100626/09/f35a4763-4b59-49c3-8061-d48fdbc29561.html如何给分和结贴?
    http://community.csdn.net/Help/HelpCenter.htm#结帖如何给自己的回帖中也加上签名?
    http://blog.csdn.net/q107770540/archive/2011/03/15/6250007.aspx
      

  13.   


    呵呵,无注释确实有点名副其实,但是一点注释不带,那绝对不可能。烦请不要玩文字游戏。
    无注释的说法实际就是将注释减到最少。
    除非一些难以理解的地方、接口、特殊返回值等。
    其他的注释可以全部省略。另外对于一些新手就算他不会使用也没有关系,
    我相信一些硬性要求写注释的项目其代码也应该有单元测试的。
    每一个单元测试实际就是一个用例。
    有了用例,只要根据用例内写好的逻辑关系是很容易知道这些函数该如何被使用的。如果连测试用例都看不懂的话,那好像,我就真的没什么好说的了。
    这已经接近于百度搜代码,复制、粘贴了。
    他只需要稍微的明白调用关系,前后顺序等等就可以了。架设那个人要改你的函数,我相信你所写的代码让他改,
    那他应该是一个起码可以理解普通代码能力的Coder。
    但他理解不了你的代码,我觉得问题不在他,是原来写代码的人身上。
    没有把代码写规范,逻辑不够清晰。现在的新技术文档基本都是英文的。连那琳琅满目的英文注释都看的明白。
    短短的几个单词的函数描述都看不懂,这是何道理呢?
      

  14.   


    程序结构应该有相关的技术文档的。
    包括项目架构、类库之间关系、程序运行过程。等等。
    不应该是注释做的事情。没有谁会因为想要清楚程序的结构而从最外层的方法F12一路跟进的。我再写个例子你瞧瞧
    private void MainForm_Load(object sender, EventArgs e)
            {
                CheckSingle();            this.WindowState = FormWindowState.Minimized;            Config.Load();
                Logger.RegisterTextBoxListener(txtLog, 500);
            }
    我相信这样一个逻辑不需要注释也完全说的过去吧。
    Load事件里做的事情大概情况如下:
    1.检查程序是否只启动了一个。
    2.窗体最小化。
    3.加载配置。
    4.注册日志到txtLog上。这些一目了然的代码我觉得加了注释也没什么意义。
    而且这个Load很明显可能会被修改。
    比如注册日志到硬盘。等等吧。
    这样不但要维护代码,还要维护注释。如果注释没跟上,反而变成累赘。
      

  15.   

    作为一个Coder,本身就应该具备看懂简单英文的本领。
    只要遵循好一个函数只做一件事。函数名即描述。函数体即逻辑。
    我相信一个合格的Coder是看的懂的。这就好比我们经常查阅英文文档一样。而这显然比看英文文档要容易的多。
      

  16.   

    如果你想把你的代码交给别人维护,那么适当的注释是必不可少的
    (注:意味着你再也拿不到BOSS一分钱了)如果你想自己来维护代码,而让别人束手无策,那就尽量不要注释  
    (注:BOSS只有找你,短期来看,钱滚滚而来,长期嘛,就要看个人能力了)
      

  17.   

    方法名长与短这个关系不大,但方法名一定要清晰
    比如:如果分类id查找所有的产品名称,并分页
    个人认为好:getProductNameListByCateIDPage
    不好:getnames,getNameList 等等
    需要方法名简单,但未能描述出方法的功能
    我个人观点:让人看到这个方法名就能知道这个方法大概实现什么功能,长一点关系不大(总不能一个方法超过100个字符吧?!)。
    至于你的方法名用什么类型,驼峰,下划线等等,看规范了------------------
    关于注释:
    像楼主所示的注释,可以基本不写,因为看方法名就知道大概了。
    但关键的,比如一个判断分支,变量名,标识等等,比较重要的(个人认为),或者后面多用到之类的,最好注释一下。-------------------
    以上是我个人观点
      

  18.   


    加了注释用中文搜索,没加注释用英文搜索,问题不大。
    只要不是 一个GetIndex 在A项目中叫GetIndex 在B里叫 GetInt 就行。
    自己大概有一个统一的规范。
    就像注释一样,两个同样的功能注释描述不一样,你也无法找到。
    这个不算是必须写注释的理由,呵呵。
      

  19.   

    我也比较支持这个风格.每个方法尽量简单.我觉得我们写代码和写文章一样,就是在描述事情.
    这个时候还需要注释,说明我们code没有把我们想表达意思表达出.当然完全没有注释,也确实行不通,比如一些算法上,别人可能不理解,这时候需要写出想法.我最近在改一个老项目的bug.经常会发现一些坑爹注释.
    如:int type;          //1:....,2:....,3:.....
    最后调来调去发现实际的东西和注释并不一样.像这种情况,可以在set方法里去常量类的数据.然后把常量的名字取得好一点.
    当然这个地方有魔鬼数字的问题,但是这个注释,引导我做了错误想法和判断.
    越到后来,越来越不想念注释了.确认有人会代码,重构代码,但不会修改注释.错误的注释比没有注释的问题要大得多,个人感觉.
    总之,首先不是考虑把代码写得尽量清晰,易懂.
    实在没有办法的时候,还是要补上注释.毕竟实际很难做到无注释编程.另外,我也看了clean code了,受了蛮多启发的.