2007年10月10日星期三

《.NET设计规范》——学习笔记(3) 命名规范

一组一致的的命名约定对框架的可用性及其重要。 名字要易于理解,同时必须传达每个元素的功能。

大小写约定

标识符的大小写规则

PascalCasing:如HtmlTag  IOStream

camelCasing:如htmlTag  ioStream

  • 要把PascalCasing用于由多个单词构成的名字空间、类型、以及成员的名字;
  • 要把camelCasing用于参数的名字。

通用命名约定

单词的选择

对框架中标识符的名字来说,很重要的一点是一目了然。

名字的意思清楚比长度短更重要。名字应该与场景、系统的逻辑组成或物理组成以及为人熟知的概念相对应,而不应该与技术或框架相对应。

  • 要为标识符选择易于阅读的名字;
  • 要更看重可读性,而不是更看重简短性;
  • 不要使用下划线、连字符以及其他任何既非字母也非数字的字符;
  • 不要使用匈牙利命名法;
  • 避免使用与广泛使用的编程语言的关键字有冲突的标识符。

使用单词缩写和首字母缩写词

一般来说,不要在标识符中使用单词缩写或首字母缩写:宁可名字长一点,也不要别人看不懂。  尤其不要使用未被广泛接受的单词缩写和首字母缩写词。

  • 不要使用缩写词或缩略词作为标识符名字的一部分
    用GetWindow  不用GetWin
  • 不要使用未被广泛接受的首字母缩写词
    多谓广泛接受:用搜索引擎在网上搜索该首字母缩写词,如果返回的前几个结果与期望相符,那么该首字母缩写词才有资格被称为众所周知。

避免使用语言特有的名字

  • 要给类型名使用语义上有意义的名字,而不要使用特有的关键字
    GetLength比GetInt要好
  • 要使用CLR的通用类型名,而不要使用语言特有的别名
  • 要使用常见的名字,比如value或item,而不要重复类型的名字

为已有API的新版本命名

当用新类型和新成员接替或取代已有的类型或成员时,如何选择名字:

  • 使用与旧API相似的名字
  • 要优先使用后缀而不是前缀来表示已有API的新版本
    这样有助于在浏览文档或使用Intellisense时发现新版本:按字母排序
  • 可以考虑使用全新但有意义的标识符
  • 要使用数字后缀来表示已有API的新版本
    有些名字(或工业标准)不宜添加后缀或改名
  • 不要在标识符中使用“Ex”“New”等类似的后缀来区分相同API的不同版本
  • 要在引入64位整数(long)而非32位整数进行操作的新版本API时使用“64”后缀,反之亦然。

程序集和DLL的命名

程序集是一个部署单元,同时还代表托管代码程序的身份。虽然程序集可以分布一个或多个文件中,但一般来说一个程序集仅与一个DLL相对应。

名字空间与DLL程序集的区别:

名字空间:一组逻辑实体

DLL和程序集:打包和部署时的一个单

  • 要为程序集和DLL选择提示性的名字,比如System.Data,这样很容易就知道它的大致功能。
  • DLL命名:<Company>.<Component>.dll

名字空间的命名

<Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]

  • 要用公司名称作为名字空间的前缀,不要用缩写
  • 要用稳定的与版本无关的产品名称作为名字空间的第二层
  • 不要根据公司的组织架构来决定名字空间的层次结构,因为公司内部组织的名称一般来说不会持续太长的时间
  • 要使用PascalCasing大小写风格,并用点号来分隔名字空间的各部分。
    如Microsoft.Office.PowerPoint
  • 考虑适当的时候在名字空间中使用复数形式。  首字母缩写词例外
    System.Collections
    System.IO
  • 不要用相同的名字来命名名字空间与位于该名字空间中的类型
    如:不要将名字空间命名为Debug,然后又在该名字空间中提供一个名为Debug的类。

名字空间和类型冲突

  • 不要引入太一般化的类型名,比如Element、Node、Log以及Message。

不同类型的名字空间,有不同的规范来避免类型名的冲突:

  • 应用程序模型名字空间(application model namespace)
    属于单个应用程序模型的名字空间经常一起使用,但是它们几乎不合属于其他应用程序模型的名字空间一起使用
    System.Windows*
    System.UI*
  • 基础设施名字空间(infrastructure namespace)
    此类别包含一些在开发常用应用程序时很少会导入的名字空间
  • 核心名字空间(core namespace)
    包含了所有的System名字空间,但应用程序模块名字空间和基础设施名字空间除外。  包括System、System.IO、System.Xml以及System.Net等等
  • 技术名字空间组(technology namespace group)
    此类别包含所有那些以相同的两个前缀(<Company>.<Technology>*)开始的名字空间。

类、结构和接口的命名

一般来说类型名应该是名词词组。如果无法为类型找到一个名词词组,那么应该重新考虑类型的总体设计。

另一个中重要的考虑因素:最易于识别的名字应该用于最常用的类型。

最常用的类型名应该反映出使用场景,而不是继承层次。

  • 要用名词词组来给类型命名。使用PascalCasing风格
  • 不要给类名加前缀
    只有接口才能(可以)被加前缀“I”,那是因为.NET框架受到COM及Java的影响
  • 考虑让派生类的名字以其基类结尾
    public class FileStream : Stream {...}
  • 要确保一对类/接口的名字只差一个“I”前缀,如果该类是该接口的标准实现。
    public interface IComponent {...}
    public class Component : IComponent {...}

泛型类型参数的命名

  • 要用描述性的名字来命名
  • 考虑用T来命名参数类型
  • 要给描述性的类型参数名加上T前缀
  • 考虑在类型参数中显示出施加于该类型参数上的限制

枚举类型的命名

  • 要用单数名词来命名枚举类型,除非它表示的是位域(bit field)
  • 不要给枚举类型的名字添加“Enum”后缀,也不要添加“Flag”、“Flags”等后缀
  • 不要给枚举类型值的名字添加前缀
    此规范与C++中通常所使用的恰好相反。在C++中给枚举的每个成员加上完成的限定符是很重要的,因为它们可能在枚举名的作用域之外被访问。但是在托管代码中,枚举成员只能通过枚举名的作用域来访问。

类型成员的命名

类型:方法、属性、事件、构造函数、字段

方法的命名

要尽量根据方法所对应的任务来给它们命名,而不要根据一些实现细节。

  • 要用动词或动词词组来命名方法

属性的命名

  • 要用名词、名词词组或形容词来命名属性
  • 不要让属性名看起来与“Get”方法的名字相似
  • 要用肯定性的短语(CanSeek而不是CantSeek)来命名布尔属性
  • 考虑用属性的类型名来命名属性
    public enum Color {...}
    public class Control
    {
        public Color Color
        {
            get {...}
            set {...}
        }
    }

事件的命名

  • 要用动词或动词短语来命名事件
    事件总是表示一些动作,要么正在发生,要么已经发生
  • 要用现在时和过去时来赋予事件名之前和之后的概念
    窗口关闭之前发生的close事件:Closing
    窗口关闭之后发生的close时间:Closed
  • 不要用Before和After前缀或后缀来区分前置和后置事件
  • 要在命名事件处理函数(用作事件类型的委托)时加上“EventHandler”后缀
  • 要在事件处理函数中用sender和e作为两个参数的名字
    sender:触发事件的对象,在整个.NET框架中,sender为object类型
  • 要在命名事件的参数类型时加上“EventArgs”后缀

字段的命名

  • PascalCasing风格
  • 名词或名词短语
  • 不要给字段名添加前缀

参数的命名

  • camelCasing风格
  • 要使用描述性的参数名
    参数名要具备足够的描述性,使得在大多数情况下,用户根据参数的名字和类型就能够确定它的意思
  • 考虑根据参数的意思而不是参数的类型来命名参数

资源的命名

本地化的资源就好比是属性,可以通过特定的对象来引用。因此,它的命名规范与属性的相似。

  • 要在命名属性关键字(resource key)时使用PascalCasing大小写风格
  • 要使标识符的名字具有描述性而不是使名字变短
  • 不要使用各主要CLR编程语言特有的关键字
  • 要在命名资源时使用字母、数字和下划线
  • 要用点号来给标识符清楚地划分层次
  • 要在为异常消息资源命名是遵循下面的命名约定:
    资源标识符应该是异常的类型名加一个简短的异常标识符,之间以点号分隔

2007年9月20日星期四

Adobe Flex 3最有趣的特征之一 :Web设计者和开发者的相遇

 

著:Yakov Fain 译:叶进

Adobe将于2008年二月左右发布Flex 3.它有增加了一些新的改进和特色,尤其是它将使Flash设计者和Flex开发者结合在一起。CS3可以很容易的使Flex融入到Flash IDE的发布时间线上。Flash中的容器创建在Flex中将会得到足够多的发展。为了开发Flash CS3,你必须要有个一个Flex组件工具包。这就使得Flash艺术拥护者必须去学习至少一点点Flex的基础知识,那会使他们获得在接下来的几年中获得更多的市场。

在今年的早些时候,我看到MS发布了Sliver流。我对这种可以被一个对于Web设计一窍不通的人开发的绚丽的GUI应用印象非常深刻。他(用时间线)创建了一个绚丽的画面,并且为Sillverlight开发者增加了添加他们自己写代码的地方。现在,Flash设计者也可以非常简单地就整合Flex为他们的作品添加代码了。

只要看看这段5分钟的youtube视频,你就知道我在说什么了。新一代的用户体验专家将获得更好的工具和动机去获得、学习新的技能。但是,针对艺术家的编程介绍应该更优雅一点——他们是不一样的人群,同时如果你用编程术语的话,可能会把他们吓跑。我对于这个有切身的体会,因为我的儿子,一个接受过专业培训的漫画家和漫画创作者对Flash IDE非常精通,却拒绝对于编程做任何事情。在很多场合,我都试图使他确信通过学习一些编程知识赚取更多的钱是一件好事。但是,到现在位置,我还没有成功,当然我会继续劝说他的,特别是当Flex 3发布的时候。那些认识到Web设计和编程开发是未来的人将会有一个无边的发展(People who realize that knowing of Web design and programming is the future will have an edge.)!

漫谈C#之关键字(1)

    每一种语言都有非常多的关键字,而且这些关键字也都大同小异,不过毕竟还是有些许的不一样。有些关键字大家碰到的多了,自然就熟悉了,但是有些关键字用得不大多,或者是新引入的,所以就不大熟悉了。我平常在用的时候,就是会碰到一些关键字,感觉有点生疏,平常也会把这些我不懂的关键字的用法了解一下并记录下来。想到应该也有很多跟我同样的人,所以就把我的记录跟大家分享一下。请各位tx多多指正!
访问关键字
    base:用于派生类中访问基类的成员

  • 调用基类上已被其他方法重写的方法

    1 public override void GetInfo()
    2 {
    3 base.GetInfo(); // 调用基类上的GetInfo方法
    4 }

  • 指定创建派生类实例时应用的基类构造函数

    1 public MyDerived() : base() // 调用基类的构造函数
    2 {}

    从静态方法中使用base关键字是错误的。
转换关键字
explicit:用于声明用户定义的显式类型转换运算符

1 class MyType
2 {
3 public static explicit operator MyType(int i)
4     {
5 // 从int转换到MyType类型的代码
6     }
7 }

显式转换运算符必须通过类型转换调用

1 int i;
2 MyType x = (MyType)i; // int到MyType类型的转换需要进行类型转换

    如果转换操作可能导致异常或信息丢失,则应用explicit关键字标记它。
    implicit:用于声明用户定义的隐式转换运算符 

1 class MyType
2 {
3 public static implicit operator int(MyType m)
4     {
5 // 从MyType转换到int类型的代码
6     }
7 }

1 MyType x;
2 int i = x;  // 隐式地调用MyType的MyType到int类型的转换运算符

隐式转换可以通过消除不必要的类型转换来提高源代码的可读性。
    一般情况下,调用某一个隐式转换时,应当绝不会引发异常,并且不会造成信息丢失。否则,应将其标记为explicit。
方法参数关键字
    如果声明方法的参数时没有指明ref或out,该参数将具有与该方法相关的值。这个值在方法中能被更改,但是当程序返回到调用过程时,这种改动不会被保留。

    params:用于指定在参数数目可变时带有参数的方法参数
    在方法声明中的params关键字之后不允许引入任何其他参数,但在其前面可以有其他参数。而且在方法声明中只允许使用一个params关键字。

 1 public static void UseParams(params int[] list)
 2 {
 3 for(int i = 0; i < list.Length; i++)
 4     {
 5         Console.WriteLine(list[i]);
 6     }
 7 }
 8
 9 public static void Main()
10 {
11     UseParams(1,2,3);
12 int[] myArray = new int[3] { 10,11,12 };
13     UseParams(myArray);
14 }

    ref、out  使方法可以引用传递到该方法的那一个变量,当程序转至调用方法时,在方法中对参数所做的任何改动都将传给该变量。
    ref参数的值将被传递到ref参数,故必须首先初始化;而out参数不然,它的值不会被传递到该out参数,故不必首先初始化,但它必须在方法返回以前为out参数赋值。
    属性不是变量,不能作为ref/out参数。

2007年9月5日星期三

《.NET设计规范》——学习笔记(2.5)框架设计基础

这篇文章在一定程度上是对前面几篇文章的一个总结。

一个成功的通用框架必须是为广大具有不同的需求、技能和背景的开发人员而设计的。框架设计师面临的最大挑战是为这些多样化的用户群提供即简单又功能强大的框架。

  • 要设计即功能强大又易于使用的框架。
    80/20原则。 要把精力集中在框架中使用最为频繁的部分(20%)
  • 要明确地为具有不同编程风格、需求、技能以及使用不同编程语言的开发人员设计框架。
  • 要了解哪些使用多语言框架的广大开发人员。
    我们往往会只为自己设计API,而没有清楚地考虑用户的真正需求。

渐进框架

针对不同的使用场景,为不同的开发团体提供不同的产品,这种多框架的方法在某种程度上说是成功的,比如MS有Visual Basic程序库,有Win32程序库,也有MFC和ATL,但它也存在严重的缺点:多框架使得使用某个框架的开发人员难以将他们的知识转移到下一个技能等级或使用场景(这通常需要另一个框架)。

  • .NET框架所做的是把VB、MFC、ATL、ASP等这些模型统一起来。无论开发人员使用何种编程语言或者选择何种编程模型,可供使用的API始终都是一致的。
    一个更好的方法是提供渐进框架(Progresive framework)。  从无到有,慢慢积累知识,并应用到以后更高级的使用场景中去。
  • .NET框架就是一个渐进框架。
    渐进框架的目标是覆盖广大的开发人员,但并不是所有可能的开发人员。
    这也应了没有十全十美这句话:不可能满足每一个开发人员的需求。

框架设计的基本原则:

对用户而言,真正的开发效率来自能够轻易地创造非凡的产品,而并非来自能够轻易地创造垃圾。

  1. 场景驱动设计原则
  2. 低门栏原则
  3. 自说明对象原则
  4. 分层架构原则

《.NET设计规范》——学习笔记(2.4)分层架构原则

分层设计使得在单个框架中同时提供强大的功能和易用性成为可能。

  • 考虑对框架进行分层,使高层API能提供最佳的开发效率,低层API能提供最强大的功能和最丰富的表现力。
    通俗地讲,象我这样的菜鸟只能用高层API,太低层都不懂,而牛人们都是想用也更愿意用低层API的强大功能的(个人意见)。ps:这边的高层跟低层不是指高深的意思。而是从易用性方面考虑的!
  • 避免把低层API和高层API混在同一名字空间中,如果低层API非常复杂的话(即包含了许多类型)。
  • 要确保单个特性域中不同的层能很好的集成在一起。

2007年9月4日星期二

《.NET设计规范》——学习笔记(2.3)自说明对象原则

在简单的使用场景中,一定要让框架无需文档就能使用。

  • 要确保API是直观的,无需查阅参考文档就能用于基本场景
    你总不希望写个“Hello World”都去查阅API文档吧。
  • 要为所有的API提供优秀的文档。
    一方面,并非所有的API都能自说明。不同的人会认为不同的API是自说明的;
    另一方面,有些人想在开始使用API之前完全理解它们。

设计自说明API时最重要的一些考虑因素:

  1. 命名
    要在规范检查中重视标识符名称的选择;
    不要担心标识符的名字太冗长;
           一眼就能看出相应的方法是做什么的,类型和参数是表示什么意思。
           而且类型的名字如果足够好,那么用到这些类型的代码会更易于立即和维护。
    要在设计过程的初期就让用户教育专家参与;
    考虑把最好的名字留给最常用的类型。
  2. 异常
    要通过异常消息来清楚地告诉开发人员对框架的误用。
  3. 强类型
    很明显,调用Customer.Name要比调用Customer.Properties["Name"]容易。
    要尽可能提供强类型API。
           如果必须使用属性包,那么应该为包中最常用的属性提供相应的强类型属性。
  4. 一致性
    要确保与.NET框架以及客户可能会使用的其他框架保持一致。
  5. 限制抽象的数量
    标准面向对象设计方法会产生大量抽象。其目的是使代码易于维护。然而如果框架中存在太多的抽象,则会要求用户对框架的架构有深入的了解,不易于用户使用。
    避免在主要场景的API中使用太多的抽象。