posts - 6, comments - 1, trackbacks - 0, articles - 1
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

2008年1月8日

集合是OOP中的一个重要概念,C#中对集合的全面支持更是该语言的精华之一。

  为什么要用泛型集合?
  在C# 2.0之前,主要可以通过两种方式实现集合:
  a.使用ArrayList
直接将对象放入ArrayList,操作直观,但由于集合中的项是Object类型,因此每次使用都必须进行繁琐的类型转换。

  b.使用自定义集合类
  比较常见的做法是从CollectionBase抽象类继承一个自定义类,通过对IList对象进行封装实现强类型集合。这种方式要求为每种集合类型写一个相应的自定义类,工作量较大。泛型集合的出现较好的解决了上述问题,只需一行代码便能创建指定类型的集合。

什么是泛型?

泛型是C# 2.0中的新增元素(C++中称为模板),主要用于解决一系列类似的问题。这种机制允许将类名作为参数传递给泛型类型,并生成相应的对象。将泛型(包括类、接口、方法、委托等)看作模板可能更好理解,模板中的变体部分将被作为参数传进来的类名称所代替,从而得到一个新的类型定义。泛型是一个比较大的话题,在此不作详细解析,有兴趣者可以查阅相关资料。

怎样创建泛型集合?
主要利用System.Collections.Generic命名空间下面的List泛型类创建集合,语法如下:

  List<T> ListOfT = new List<T>();
其中的"T"就是所要使用的类型,既可以是简单类型,如string、int,也可以是用户自定义类型。下面看一个具体例子。

定义Person类如下:
class Person
{
private string _name; //姓名
private int _age; //年龄

//创建Person对象
public Person(string Name, int Age)
{
this._name= Name;
this._age = Age;
}

//姓名
public string Name
{
get { return _name; }
}

//年龄
public int Age
{
get { return _age; }
}
}

//创建Person对象
Person p1 = new Person("张三", 30);
Person p2 = new Person("李四", 20);
Person p3 = new Person("王五", 50);

//创建类型为Person的对象集合
List<Person> persons = new List<Person>();

//将Person对象放入集合
persons.Add(p1);
persons.Add(p2);
persons.Add(p3);

//输出第2个人的姓名
Console.Write(persons[1].Name);
  可以看到,泛型集合大大简化了集合的实现代码,通过它,可以轻松创建指定类型的集合。非但如此,泛型集合还提供了更加强大的功能,下面看看其中的排序及搜索。

  泛型集合的排序
  排序基于比较,要排序,首先要比较。比如有两个数1、2,要对他们排序,首先就要比较这两个数,根据比较结果来排序。如果要比较的是对象,情况就要复杂一点,比如对Person对象进行比较,则既可以按姓名进行比较,也可以按年龄进行比较,这就需要确定比较规则。一个对象可以有多个比较规则,但只能有一个默认规则,默认规则放在定义该对象的类中。默认比较规则在CompareTo方法中定义,该方法属于IComparable<T>泛型接口。请看下面的代码:
class Person :IComparable<Person>
{
//按年龄比较
public int CompareTo(Person p)
{
return this.Age - p.Age;
}
}

  CompareTo方法的参数为要与之进行比较的另一个同类型对象,返回值为int类型,如果返回值大于0,表示第一个对象大于第二个对象,如果返回值小于0,表示第一个对象小于第二个对象,如果返回0,则两个对象相等。

定义好默认比较规则后,就可以通过不带参数的Sort方法对集合进行排序,如下所示:

//按照默认规则对集合进行排序
persons.Sort();

//输出所有人姓名
foreach (Person p in persons)
{
Console.WriteLine(p.Name); //输出次序为"李四"、"张三"、"王五"
}
实际使用中,经常需要对集合按照多种不同规则进行排序,这就需要定义其他比较规则,可以在Compare方法中定义,该方法属于IComparer<T>泛型接口,请看下面的代码:
class NameComparer : IComparer<Person>
{
//存放排序器实例
public static NameComparer Default = new NameComparer();

//按姓名比较
public int Compare(Person p1, Person p2)
{
return System.Collections.Comparer.Default.Compare(p1.Name, p2.Name);
}
}
Compare方法的参数为要进行比较的两个同类型对象,返回值为int类型,返回值处理规则与CompareTo方法相同。其中的Comparer.Default返回一个内置的Comparer对象,用于比较两个同类型对象。

下面用新定义的这个比较器对集合进行排序:


//按照姓名对集合进行排序
persons.Sort(NameComparer.Default);

//输出所有人姓名
foreach (Person p in persons)
{
Console.WriteLine(p.Name); //输出次序为"李四"、"王五"、"张三"
}

还可以通过委托来进行集合排序,首先要定义一个供委托调用的方法,用于存放比较规则,可以用静态方法。请看下面的代码:

class PersonComparison
{
//按姓名比较
public static int Name(Person p1, Person p2)
{
return System.Collections.Comparer.Default.Compare(p1.Name, p2.Name);
}
}

方法的参数为要进行比较的两个同类型对象,返回值为int类型,返回值处理规则与CompareTo方法相同。然后通过内置的泛型委托System.Comparison对集合进行排序:

System.Comparison<Person> NameComparison = new System.Comparison<Person>(PersonComparison.Name);
persons.Sort(NameComparison);

//输出所有人姓名
foreach (Person p in persons)
{
Console.WriteLine(p.Name); //输出次序为"李四"、"王五"、"张三"
}

可以看到,后两种方式都可以对集合按照指定规则进行排序,但笔者更偏向于使用委托方式,可以考虑把各种比较规则放在一个类中,然后进行灵活调用。

  泛型集合的搜索

  搜索就是从集合中找出满足特定条件的项,可以定义多个搜索条件,并根据需要进行调用。首先,定义搜索条件,如下所示:


class PersonPredicate
{
//找出中年人(40岁以上)
public static bool MidAge(Person p)
{
if (p.Age >= 40)
return true;
else
return false;
}
}
上面的搜索条件放在一个静态方法中,方法的返回类型为布尔型,集合中满足特定条件的项返回true,否则返回false。然后通过内置的泛型委托System.Predicate<T>对集合进行搜索:


System.Predicate<Person> MidAgePredicate = new System.Predicate<Person>(PersonPredicate.MidAge);
List<Person> MidAgePersons = persons.FindAll(MidAgePredicate);

//输出所有的中年人姓名
foreach (Person p in MidAgePersons)
{
Console.WriteLine(p.Name); //输出"王五"
}

泛型集合的扩展
如果要得到集合中所有人的姓名,中间以逗号隔开,那该怎么处理?
考虑到单个类可以提供的功能是有限的,很自然会想到对List<T>类进行扩展,泛型类也是类,因此可以通过继承来进行扩展。请看下面的代码:

//定义Persons集合类
class Persons : List<Person>
{
//取得集合中所有人姓名
public string GetAllNames()
{
if (this.Count == 0)
return "";

string val = "";
foreach (Person p in this)
{
val += p.Name + ",";
}

return val.Substring(0, val.Length - 1);
}
}

//创建并填充Persons集合
Persons PersonCol = new Persons();
PersonCol.Add(p1);
PersonCol.Add(p2);
PersonCol.Add(p3);

//输出所有人姓名
Console.Write(PersonCol.GetAllNames()); //输出“张三,李四,王五”

小结:
本文着重于介绍运用C# 2.0中的泛型来实现集合,以及对集合功能进行扩展,恰当的运用泛型集合,可以减少很多重复工作,极大的提高开发效率。实际上,集合只不过是泛型的一个典型应用,如果想了解更多关于泛型的知识,可以查阅其他相关资料。希望本文对你有用:-)

posted @ 2008-01-08 23:30 岑恩武 阅读(975) 评论(1) 编辑

1、命名约定Pascal和Camel命名约定
编程的命名方式主要有Pascal和Camel两种(Pascal:每个单词的首字母大写,例如ProductType;Camel:首个单词的首字母小写,其余单词的首字母大写,例如productType)
以下是一些常用的C#成员及其推荐命名方法:
标志符                                                规则                                     实例与描述
类class                                                Pascal
Application枚举类型enum                      Pascal                                   记住,是以Pascal命名,切勿包含Enum,否则FXCop会抛出Issue
委托delegate                                       Pascal                                   以Pascal命名,不以任何特殊字符串区别于类名、函数名
常量const                                           全部大写                               全部大写,单词间以下划线隔开
接口interface                                       Pascal                                  IDisposable 注:总是以 I 前缀开始,后接Pascal命名
方法function                                        Pascal                                  ToString
命名空间namespace                              Pascal                                  以.分隔,当每一个限定词均为Pascal命名方式,比如:using ExcelQuicker.Framework
参数Camel首字母小写
局部变量                                             Camel                                   也可以加入类型标识符,比如对于System.String类型,声明变量是以str开头,string strSQL = string.Empty;
数据成员                                             Camel                                   以m开头+Pascal命名规则,如mProductType(m意味member)
属性                                                   Pascal

1.1、局部变量命名在primitive的局部变量命名时,使用Camel命名规则,
比如:int type = 0;
double count = 0;

对于string类型定义,通常使用str前缀+Pascal命名的方式,
比如string strSql = ""; //这是一种典型的命名SQL语句字符串的方式。
而对于此外的类型对象定义,通常的做法是使用obj前缀+Pascal命名的方式,来告知我们这个变量是一个对象。或者也可以直接使用类名的Camel命名规则。
比如:Application objApplication = new Application();
            Application application = new Application();

1.2、参数命名Camel命名规则,首字母小写

1.3、类数据成员/属性命名数据成员命名以Camel命名方式,而属性以Pascal命名。通常如果数据成员与属性成对的话,数据成员与属性的命名区别仅在于变量名的第一个字母是小写还是大写。
比如
class Appcalition
{
        private ArrayList worksheetCollection = new ArrayList();
        public ArrayList WorksheetCollection
        {
            get
            {
                return this.worksheetCollection;
            }
        }
}
另外,类的成员数据/方法调用时,应该加上this限定符,this在编辑环境中是蓝色的,更利于我们区分局部变量、参数或静态变量,并且利于FXCop检测区分。(如果使用FxCop扫描和检测代码的话)

1.4、命名空间命名在dot之间的各限定字符串符合Pascal格式

1.5、委托缩写委托的命名方式我常常以Pascal命名,并且在命名的后面加EventHandler
比如public delegate void MouseEventHandler (object sender, MouseEventArgs e);    //用于处理与鼠标相关的事件或委托
对于自定义的委托,其参数第一个建议仍然使用object sender,sender代表触发这个时间或委托的源对象。而第二个参数继承于EventArgs类,并且在派生类中实现自己的业务逻辑。

1.6、自定义异常类自定义异常类以Exception结尾,并且在类名中能清楚的描述出该异常的原因。比如NotFoundFileException,描述出了某个实体(文件、内存区域等)无法被找到。
1.7、枚举枚举的命名是Pascal命名,不需要在枚举中加入Enum,枚举的名称能清楚的表明该枚举的用途。

1.8、常量命名全部大写,单词间并且以下划线间隔,如public const int LOCK_SECONDS = 3000;  虽然在MSDN中常量的命名推荐使用Pascal,但是从C++沿袭的命名规则来看,将常量全部大写更加能清楚的表示常量与普通变量之间的区别。

1.9、命名缩写在一般情况下,不推荐缩写命名,不要担心变量命名长,长的变量名能使变量的意义更加清晰,其实从长变量名的负面作用三,因为Ctrl+C和Ctrl+V加上在VS中的智能感知,其负面追用已经很小。变量命名的原则是,尽最大努力让其他人在看到我们的变量/函数/…等的第一时间,大概能猜出它是做什么的。
比如:int productTypeCount = 0; //我们在第一时间就能知道它是记录产品的数量的变量
而对于糟糕的命名方式:int prodTypeCount = 0; //它是productTypeCount的简写,我们一部分人也许知道prod是product的缩写,但是每人能保证所有的人都知道它。我个人认为:最优秀的代码它本身就是注释。作为一流的程序员。并不仅仅实现功能,而是要让我们的代码更加优美,具备让他人维护或今后扩充的能力。作为现在的业务系统,其门槛的准入水平已大大降低,实现功能上的需求已没有什么难度,但是高手和菜鸟的区别在于,高手的代码通俗易懂,在整个编码的过程中,不仅能考虑到性能、还会考虑代码可读性和维护性。
1.10、数据库命名数据库的字段、表名的命名都推荐采用Pascal命名方式,尽量不采用缩写。当然,使用长的字段名、表名,可能会使SQL语句的编写带来负面影响。我推荐大家可以使用一些ORM,ORM的性能肯定不会比直接写SQL的好,但是如果做业务系统,更重要的是系统多久能交付用户使用,ORM不仅使开发时间可以缩短不少,并且在后期的维护上也比直接写SQL便利很多。
2、注释规范2.1、文件头部注释在代码文件的头部进行注释,这样做的好处在于,我们能对代码文件做变更跟踪。在代码头部分标注出创始人、创始时间、修改人、修改时间、代码的功能,这在团队开发中必不可少,它们可以使后来维护/修改的同伴在遇到问题时,在第一时间知道他应该向谁去寻求帮助,并且知道这个文件经历了多少次迭代、经历了多少个程序员的开发和修改。
样本:
/********************************************************************************
** 作者: Eunge
** 创始时间: 2004-6-8
** 修改人:Lucy
** 修改时间:2004-12-9
** 修改人:Lucy
** 修改时间:2005-01-29
** 描述:
**    主要用于产品信息的资料录入,…
*********************************************************************************/

2.2、函数、属性、类等注释请使用///三斜线注释,这种注释是基于XML的,不仅能导出XML制作帮助文档,而且在各个函数、属性、类等的使用中,编辑环境会自动带出注释,方便你的开发。以protected,protected Internal,public声明的定义注释都建议以这样命名方法。
例如:
/// <summary>
/// 用于从ERP系统中捞出产品信息的类
/// </summary>
class ProductTypeCollector
{
       …
}

2.3、逻辑点注释在我们认为逻辑性较强的地方加入注释,说明这段程序的逻辑是怎样的,以方便我们自己后来的理解以及其他人的理解,并且这样还可以在一定程度上排除BUG。在注释中写明我们的逻辑思想,对照程序,判断程序是否符合我们的初衷,如果不是,则我们应该仔细思考耀修改的是注释还是程序了…

3、排版我的排版原则与建议:
1、  每行语句至少占一行,如果语句过长(超过一屏),则该语句断为两行显示;
2、  把相似的内容放在一起,比如数据成员、属性、方法、事件等,并适当的使用#region…#endregion,我最喜欢把机器生成的代码都放在一个#region里面,比如在编写ASP.NET程序时,对应自动产生的控件定义,我常用#region Automatic Generated Web Components … #endregion把他们框住
3、  使用空格,
(1)       双目操作符的前后加空格(+, =, && 等),index = index + 1;
(2)       单目操作符前加空格(!, ++, ~ 等), index ++;
(3)       逗号、分号只在后面加空格
4、  使用空行,在一段功能代码、或者函数、属性之间插入空行,这样会很直观。
在Visual Studio 2005中,其实已经带有代码格式化这样的功能,快捷键是Ctrl+K -> Ctrl+D。

4、界面控件命名我的建议是使用默认控件名作为前缀,前缀名称全部小写,这样的好处是不必为未知的控件统一命名方式发愁,比如对于Label标签控件,有的人用缩写lbl,有的人用lab,有的人用lb。这样其实仍然是避免使用缩写,有的时候仍然会使命名变得冗长,但是命名更加能反应出变量的意义,并且各个开发人员也能更好的执行,因为他们不需要去背记各个变量的缩写。
protected System.Web.UI.WebControls.Button buttonQuery;
protected System.Web.UI.WebControls.DropDownList dropdownlistProductType;
protected System.Web.UI.WebControls.TextBox textboxManufactureDate;

5、代码可读性一些建议(1)注意运算符的优先级,我们应该尽量使用括号明确表达式的操作顺序,避免使用默认优先级,给我们以及维护人带来困扰
(2)避免使用不易理解的数字,用有意义的标识来替代(枚举和常量)
比如:
if(productType == 0)
       …
else if (productType == 1)
       …
(不推荐使用)
if(productType == ProductType.CD)
       …
else if (productType == ProductType.DVD)
       …
(推荐使用)

(3)在界面层中尽量使用异常处理try语句,不要将系统级别的错误直接暴露给用户,而更应该的是把系统抛出的错误信息记录到LOG日志文件中去,告诉用户友好的提示信息
在Visual Studio 2005里面,有代码布局格式化功能,蛮有用的。其实代码的规范是为了使系统具有整体一致的编码风格,以使后期维护人员能更快的读懂代码并进行维护。我认为代码规范有其必要性,但不能因为规范而规范,从开发而言,开发是为了更快的做出稳定的系统,而稳定的系统是为了给公司带来受益。开发人员、项目管理人员都应该更多的从项目经营的角度出来,同时站在公司、客户的角度考虑问题,而不是因为代码而代码。

ADO.NET:

Connection

con

conNorthwind

Command

cmd

cmdReturnProducts

Parameter

parm

parmProductID

 DataAdapter

dad

dadProducts

DataReader

dtr

dtrProducts

DataSet

dst

dstNorthWind

DataTable

dtbl 

dtblProduct

DataRow

drow

drowRow98

DataColumn

dcol 

dcolProductID

DataRelation

drel

drelMasterDetail

DataView

dvw 

dvwFilteredProducts


ASP.NET

AdRotator

adrt

Example

Button

btn

btnSubmit

Calendar

cal

calMettingDates

CheckBox

chk

chkBlue

CheckBoxList

chkl

chklFavColors

CompareValidator

valc

valcValidAge

CustomValidator

valx

valxDBCheck

DataGrid

dgrd

dgrdTitles

DataList

dlst

dlstTitles

DropDownList

drop

dropCountries

HyperLink

lnk

lnkDetails

Image   

img

imgAuntBetty

ImageButton

ibtn

ibtnSubmit

Label

lbl

lblResults

LinkButton

lbtn

lbtnSubmit

ListBox

lst

lstCountries

Panel 

pnl

pnlForm2

PlaceHolder

plh

plhFormContents

RadioButton

rad

radFemale

RadioButtonList

radl

radlGender

RangeValidator

valg

valgAge

RegularExpression

vale

valeEmail_Validator

Repeater

rpt

rptQueryResults

RequiredFieldValidator 

valr          

valrFirstName

Table   

tbl        

tblCountryCodes

TableCell    

tblc    

tblcGermany

TableRow   

tblr    

tblrCountry

TextBox   

txt    

txtFirstName

ValidationSummary 

vals   

valsFormErrors

XML   

xmlc     

xmlcTransformResults

posted @ 2008-01-08 23:18 岑恩武 阅读(208) 评论(0) 编辑