C#开发代码的小技巧
1. 使用as,而非is
2. 使用DataReader读取数据
3. 尽量使用强类型集合(包括泛型集合),而非DataTable
4. 使用StringBuilder操作频繁变动的字符串,但以下情况例外
5.刚给control写了个扩展方法,按回车发送tab键的 ^_^
6.遍歷control
7
1)对所有类都重写ToString()方法,这样在调试,绑定中都非常有用。
2)使用Enum代替奇迹数
8找出是否存在某個窗體FORM
9.將datagridview的某個checkbox不顯示
10.打開某個路徑下的程序
11.再来一些自认为比较高级的:
1). 字符串留用技术
字符串留用技术用来处理大量的字符串,而这些字符串中又会有许多重复的字符,例如:
"str1", "str2", ... , "str100" 共10,000个这样的字符,毫无疑问,其实就99个字符
这种情况可以使用到字符串留用技术,会提到性能
2). 数组永远是0基的
3). 使用多线程或异步操作的时候使用线程池的QueueUserWorkItem()方法将需要执行的任务排队,而不是手动去Start一个线程
12.
14.在写公共类库,使用catch时,最好不要catch(Exception e){throw e;}而使用catch{throw;},同时如果使用catch捕捉异常,最好使用catch(最具体的Exception e){...}catch(最具体的Exception e){...}...一直到所有确认的错误都能检查到 ;而不要使用catch(基类Exception e){...}
15.推荐使用using(){}
去写实现了IDisposable的对象
非常漂亮!
16.如果实现了IDisposable的对象在访问完成后要进行关闭,则在try{...}finally{...//关闭代码},或直接调用using(...){...}。
17.
18.Effective C#
原则5:始终提供ToString()(部分翻译)
Always Provide ToString()
在.Net世界里,用得最多的方法之一就是System.Object.ToStrying()了。你应该为你所有的客户写一个“通情达理”的类(译注:这里是指这个类应该对用户友好)。要么,你就迫使所用类的用户,去使用类的属性并添加一些合理的易读的说明。这个以字符串形式存在,关于你设计的类的说明,可以很容易的向你的用户显示一些关于对象的信息到:Windows Form里,Web Form里,控制台输出。这些字符说明可以用于调试。你写的任何一种类型,都应该合理的重写这个方法。当你设计更多的复杂的类型时,你应该实现应变能力更强的IFormattable.ToString(). 承认这个:如果你不重写(override)这个常规的方法,或者只是写一个很糟糕的,你的客户将不得不为你修正它。
System.Object版的ToString()方法只返回类型的名字。这并没有太多有用的信息:“Rect”,“Point”,“Size”并不会如你所想的那样显示给你的用户。但那只是在你没有为你的类重写ToString()方法时得到的。你只用为你的类写一次,但你的客户却会使用很多次。当你设计一个类时,多添加一点小小的工作,就可以在你或者是其他人每次使用时得到回报。
(译注:废话!)
======================
这一原则就不翻译了,看的有点郁闷。就是ToString()的几个重写版本。以及一些格式化输出。我觉得本书不应该讨论这些入门级的内容,所以只是读了一遍,就没有全部翻译。
大家知道要重写它就行了,最好是提供几个重载版本。回头有时间再翻译这一原则的其它内容。
给一点个人建议,一般不会在一个类的ToString上提供很多的说明,给一个名字就已经足够了,然后加一个SDK帮助。更多时候,在后面添加成员类的说明。我就在一个第三方库的ToString上看到很严谨的结构,都是在类名后面,添加一些内容和重要属性的说明。
=========================================补译:
让我们来考虑一个简单的需求:重写System.Object.ToString()方法。你所设计的每一个类型都应该重写ToString()方法,用来为你的类型提供一些最常用的文字说明。考虑这个Customer类以及它的三个成员(fields)(译注:一般情况,类里的fields译为成员,这是面向对象设计时的概念,而在与数据库相关的地方,则是指字段):
默认继承自System.Object的ToString()方法会返回"Customer"。这对每个人都不会有太大的帮助。就算ToString()只是为了在调试时使用,也应该更灵活(sophisticated)一些。你重写的ToString()方法应该返回文字说明,更像是你的用户在使用这个类一样。在Customer例子中,这应该是名字:
如果你不遵守这一原则里的其它意见,就按照上面的方法为你所定义的所有类型重写该方法。它会直接为每个人省下时间。
当你负责任的为Object.ToString()方法实现了重写时,这个类的对象可以更容易的被添加到Windows Form里,Web Form里,或者打印输出。 .NET的FCL使用重载的Object.ToString()在控件中显示对象:组合框,列表框,文本框,以及其它一些控件。如果你一个Windows Form或者Web Form里添加一个Customer对象的链表,你将会得到它们的名字(以文本)显示出来(译注:而不是每个对象都是同样的类型名)。
Syste.Console.WriteLine()和System.String.Formate()在内部(实现的方法)是一样的。任何时候,.Net的FCL想取得一个customer的字符串说明时,你的customer类型会提供一个客户的名字。一个只有三行的简单函数,完成了所有的基本需求。
这是一个简单的方法,ToString()还可以以文字(输出的方法)满足很多用户自定义类型的需求。但有些时候,你的要求可能会更多。前面的customer类型有三个成员:名字,收入和联系电话。对System.Object.ToString()(译注:原文这里有误,掉了Object)的重写只使用了_name。你可以通过实现IFormattable(这个接口)来弥补这个不足。这是一个当你需要对外输出格式化文本时使用的接口。IFormattable包含一个重载版的ToString()方法,使用这个方法,你可以为你的类型信息指定详细的格式。这也是一个当你要产生并输出多种格式的字符串时要使用的接口。customer类就是这种情况,用户将希望产生一个报表,这个报表包含了已经表格化了的用户名和去年的收入。IFormattable.ToString()方法正合你意,它可以让用户格式化输出你的类型信息。这个方法原型的参数上一包含一个格式化字符串和一个格式化引擎:
你可以为你设计的类型指定要使用的格式字符串。你也可以为你的格式字符串指定关键字符。在这个customer的例子中,你可以完全可以用n来表示名字,r表示收入以及p来表示电话。这样一来,你的用户就可以随意的组合指定信息,而你则须要为你的类型提供下面这个版本的的IFormattable.ToString():
添加了这样的函数后,你就让用户具有了可以这样指定customer数据的能力:
任何对IFormattable.ToString()的实现都要指明类型,但不管你在什么时候实现IFormattation接口,你都要注意处理大小写。首先,你必须支持能用格式化字符:“G”。其次,你必须支持两个空格式化字符:""和null。当你重载Object.ToString()这个方法时,这三个格式化字符应该返回同样的字符串。.Net的FCL经常用null来调用IFormattable.ToString()方法,来取代对Object.ToString()的调用,但在少数地方使用格式符"G"来格式化字符串,从而区别通用的格式。如果你添加了对IFormattable接口的支持,并不再支持标准的格式化,你将会破坏FCL里的字符串的自动(隐式)转换。
IFormattable.ToString()的第二个参数是一个实现了IFormatProvider接口的对象。这个对象为用户提供了一些你没有预先设置的格式化选项(译注:简单一点,就是你可以只实现你自己的格式化选项,其它的默认由它来完成)。如果你查看一下前面IFormattable.ToString()的实现,你就会毫不犹豫的拿出不计其数的,任何你喜欢的格式化选项,而这些都是的格式化中所没有的。支持人们容易阅读的输出是很自然的事,但不管你支持多少种格式,你的用户总有一天会想要你预先没想到的格式。这就为什么这个方法的前几行要检察实现了IFormatProvider的对象,并把ICustomFormatter的工作委托给它了。
让我们把(讨论的)焦点从类的作者转移到类的使用者上来。你发现你想要的格式化不被支持。例如,你有一个一组客户,他们的名字有的大于20个字符,并且你想修改格式化选项,让它支持50个字符长的客户名。这就是为什么IFormatProvider接口要存在。你可以设计一个实现了IFormatProvider的类,并且让它同时实现ICustomFormatter接口用于格式化输出。IFormatProvider接口定义了一个方法:GetFormat()。这个方法返回一个实现了ICustomFormatter接口的对象。由ICustomFormatter接口的指定方法来完成实际的格式化工作。下面这一对(接口)实现了对输出的修改,让它可以支持50个字符长的用户名:
GetFormat()方法取得一个实现了ICustomFormatter接口的对象。而ICustomFormatter.Format()方法,则根据用户需求负责实际的格式化输出工作。这个方法把对象转换成格式化的字符串。你可以为ICustomFormatter.Format()定义格式化字符串,因此你可以按常规指定多重格式。FormatProvider就是一个由GetFormat()方法取得的IFormatProvider对象。
为了满足用户的格式化要求,你必须用IFormatProvider对象明确的调用string.Format()方法:
Console.WriteLine( string.Format( new CustomFormatter(), "", c1 ));
你可以设计一个类,让它实现IFormatProvider和ICustomFormatter接口,再实现或者不实现IFormattable 接口。因此,即使这个类的作者没有提供合理的ToStrying行为,你可以自己来完成。当然,从类的外面来实现,你只能访问公共属性成数据来取得字符串。实现两个接口,IFormatProvider 和 IcustomFormatter, 只做一些文字输出,并不需要很多工作。但在.Net框架里,你所实现的指定的文字输出在哪里都可以得到很好的支持。
所以,再回到类的作者上来。重写Object.ToString(),为你的类提供一些说明是件很简单的事。你每次都应该为你的类型提供这样的支持。而且这应该是对你的类型最显而易见的,最常用的说明。在一些极端情况下,你的格式化不能支持一些过于灵活的输出时,你应该借用IFormattable接口的优势。它为你的类型进行自定义格式化输出提供了标准方法。如果你放弃这些,你的用户将失去用于实现自定义格式化的工具。这些解决办法须要写更多的代码,并且因为你的用户是在类的外面的,所以他们无法检查类的里面的状态。
最后,大家注意到你的类型的信息,他们会明白输出的文字。尽可能以简单的方式的提供这样的信息吧:为你的所有类型重写ToString()方法。
18.条款1:使用属性代替可访问的数据成员
条款2:运行时常量(readonly)优于编译时常量(const)
条款3:操作符is或as优于强制转型
条款4:使用Conditional特性代替#if条件编译
条款5:总是提供ToString()方法
条款6:明辨值类型和引用类型的使用场合
条款7:将值类型尽可能实现为具有常量性和原子性的类型
条款8:确保0为值类型的有效状态
条款9:理解几个相等判断之间的关系
条款10:理解GetHashCode()方法的缺陷
条款11:优先采用foreach循环语句
条款12:变量初始化器优于赋值语句
条款13:使用静态构造器初始化静态类成员
条款14:利用构造器链
条款15:利用using和try/finally语句来清理资源
条款16:尽量减少内存垃圾
条款17:尽量减少装箱与拆箱
条款18:实现标准Dispose模式
条款19:定义并实现接口优于继承类型
条款20:明辨接口实现和虚方法重写
条款21:使用委托表达回调
条款22:使用事件定义外发接口
条款23:避免返回内部类对象的引用
条款24:声明式编程优于命令式编程
条款25:尽可能将类型实现为可序列化的类型
条款26:使用IComparable和IComparer接口实现排序关系
条款27:避免ICloneable接口
条款28:避免强制转换操作符
条款29:只有当新版基类导致问题时才考虑使用new修饰符
条款30:尽可能实现CLS兼容的程序集
条款31:尽可能实现短小简洁的函数
条款32:尽可能实现小尺寸、高内聚的程序集
条款33:限制类型的可见性
条款34:创建大粒度的Web API
条款35:重写优于事件处理器
条款36:合理使用.NET运行时诊断
条款37:使用标准配置机制
条款38:定制和支持数据绑定
条款39:使用.NET验证
条款40:根据需要选用恰当的集合
条款41:DataSet优于自定义结构
条款42:利用特性简化反射
条款43:避免过度使用反射
条款44:为应用程序创建特定的异常类
条款45:优先选择强异常安全保证
条款46:最小化互操作
条款47:优先选择安全代码
条款48:掌握相关工具与资源
条款49:为C# 2.0做准备
条款50:了解ECMA标准
19.使用数据库时,存储过程参数,尽量不要拼接字符串。
数据库要建立索引、使用事务。
ASP.NET合理利用母版页和缓存。
提取字符串注意正则表达式的优化,提高准确性来减少迭代。
算术表达式要多用小括号。
private bool test = false;if(test){;}
结构简单时使用三目运算符。
灵活使用switch。
20.尽量使用托管代码。
平时写的代码要OOP,然后封装成自己的类库,提高日后开发效率。
多用组合,少用继承。
字符串合理使用Trim()。
实现文件复制时,尽量减少对磁盘读写次数。
21.换行字符串,相当于"\r\n",Environment.NewLine
contains代替indexof
字符串组合使用StringBuilder,减少字符串拷贝
使用接口来编程
使用属性代替原始数据
22.先通过
System.Diagnostics.Process类获取想要编辑的进程
调用API
打开进程
m_ReadProcess.Id 进程的ID编号
读取
IntPrt MemoryAddress 为要读取的内存地址
uint bytesToRead 需要读的数量
out int bytesRead 实际读出的数量
写入
24.
选择is或者as操作符而不是做强制类型转换
C#是一个强数据类型语言。好的编程实践意味着当可以避免从一种数据类型强制转化为另种数据类型时,我们应该尽我们的所能来避免它。但在某些时候,运行时类型检测是不可避免的。在C#里,大多数时候你要为调用函数的参数使用System.Object类型,因为Framwork已经为我们定义了函数的原型。你很可能要试图把那些类型进行向下转化为其它类型的接口或者类。你有两个选择:用as运算符,或者,采用旧式的C风格,强制转换。(不管是哪一种,)你还必须对变量进行保护:你可以试着用is进行转换,然而再用as进行转换或者强制转换。
无论何时,正确的选择是用as运算符进行类型转换。因为比起盲目的强制转换它更安全,而且在运行时效率更高。用as和is运算符进行转换时,并不是对所有的用户定义的类型都能完成的。它们只在运行时类型和目标类型匹配的时候,转换才能成功。它们决不会构造一个新的对象来满足(转化)要求。
看一个例子。你写了一段代码,要转换一个任意类型的对象实例到一个MyType类型的实例。你是这样写代码的:
或者你这样写:
你会同意第一种写法更简单更容易读。它没有try/catch结构,所以你可以同时避免(性能)开销和(多写)代码。我们注意到,强制转换的方法为了检测转换是否把一个null的对象进行强制转换,而不得不添加一个捕获异常的结构。null可以被转换为任意的引用类型,但as操作符就算是转化一个null的引用时,也会(安全的)返回一个null。所以当你用强制类型转换时,就得用一个try/catch结构来捕获转换null时的异常。用as进行转换的时就,就只用简单的检测一下转化后的实例不为null就行了。
(译注:被转换对象和转换后的结果都有可能为null,上面就是对这两种null进行了说明,注意区分。强制转换是不安全的,可能会有异常抛出,因此要用try/catch结构来保证程序正常运行,而as转换是安全的,不会有异常抛出,但在转换失败后,其结果为null)
强制转换与as转换最大的区别表现在如何对待用户定义类型的转换。
与其它运算不一样,as和is运算符在运行时要检测转换目标的类型。如果一个指定对象不是要求转换的类型,或者它是从要求转换类型那里派生的,转换会失败。另一方面,强制转换可以用转换操作把一个对象转换成要求的类型。这还包括对内置数据(built-in numberic)类型的转换。强制转换一个long到一个short可能会丢失数据。
同样的问题也隐藏在对用户定义类型的转换上。考虑这样的类型:
假设代码片段中开始的Factory.GetObject()函数返回的是SecondType 类型的数据:
如果你使用下面的代码段,你应该可以成功的完成从SecondType到MyType的转换:
你决不应该写出如果糟糕的代码,但它确实解决了一个很常见的难题。尽管你决不应该这样写代码,但你可以写一个函数,用一个System.Object参数来完成正确的转换:
记住,对一个用户定义类型的对象,转换操作只是在编译时,而不是在运行时。在运行时存在介于o2和MyType之间的转换并没有关系,(因为)编译器并不知道也不关心这些。这样的语句有不同的行为,这要取决于对st类型的申明:
t = ( MyType ) st;
(译注:上面说的有些模糊。为什么上面的代码可能会有不同的行为的呢?不同的什么行为呢?主要就是:上面的这个转化,是在编译时还是在运行时!如果st是用户定义的类型,那么上面的转换是在编译时。编译器把st当成为System.Object类型来编译生成的IL代码,因此在运行时是无法把一个Object类型转化为MyType类型的。解决办法就是前面提到的方法,多加一条语句,先把Object类型转换为SecondType,然后再强制转化为MyType类型。但是如果st是内置类型,那么它的转换是在运行时的,这样的转化或许会成功,看后面的说明。因此,类似这样的代码:MyType m_mytype = (m_secondType as SecondType) as MyType;是不能通过编译的,提示错误是无法在编译时把SecondType转化为MyType,即使是重写了转换操作符。)
但下面的转换只会有一种行为,而不管st是什么类型。
所以你应该选择as来转换对象,而不是强制类型转换。实际上,如果这些类型与继承没有关系,但是用户自己定义的转换操作符是存在的,那么下面的语句转换将会得到一个编译错误:t = st as MyType;现在你应该明白要尽可能的使用as,下面我们来讨论不能使用as的时候。as运算符对值类型是无效的,下面的代码无法通过编译:
这是因为整形(ints)数据是值类型,并且它们永远不会为null。当o不是一个整形的时候,i应该取什么值呢?不管你选择什么值,它都将是一个无效的整数。因此,as不能使用(在值类型数据上)。你可以坚持用强制转化:
但是你并没有必要这样坚持用强制转换。你可以用is语句取消可能因转换引发的异常:
(译注:is和as一样,都是类型转换安全的,它们在任何时候都不会在转换时发生异常,因此可以先用is来安全的判断一下数据类型。与as不同的时,is只是做类型检测并返回逻辑值,不做转换。)
如果o是其它可转化为整形的类型(译注:但o并不是真正的整形),例如double,那么is运算操作会返回false。对于null,is总是返回false。
is只应该在你无法用as进行转换时使用。
另外,这是无意义的冗余:
如果你写下面的代码,那么跟上面一样,都是冗余的:
这都是低效且冗余的。如果你使用as来转换数据,那么用is来做检测是不必要的。只用检测返回类型是否为null就行了,这很简单。
现在,你已经知道is,as和强制转换之间的区别了。而在foreach的循环中,是使用的哪一种转换呢?
foreach循环是用强制转换来完成把一个对象转换成循环可用的类型。上面的循环代码与下面手写的代码(hand-coded)是等效的:
foreach须要用强制转换来同时支持对值类型和引用类型的转换。通过选择强制转化,foreach循环就可以采用一样的行为,而不用管(循环)目标对象是什么类型。不管怎样,因为foreach循环是用的强制转换,因些它可能会产生BadCastExceptions的异常。
因为IEnumberator.Current返回一个System.Obejct对象,该对象没有(重写)转换操作符,所以它们没有一个满足(我们在上面做的)测试。
(译注:这里是说,如果你用一个集合存放了SecondType,而你又想用MyType来对它进行foreach循环,那么转换是失败的,原因是在循环时,并不是用SecondType,而是用的System.Object,因此,在foreach循环里做的转换与前面说的:MyType t = ( MyType ) o;是一样的错误,这里的o是SecondType,但是是以System.Object存在。)
正如你已经知道的,一个存放了SecondType的集合是不能在前面的函数UseCollection中使用循环的,这会是失败的。用强制转换的foreach循环不会在转换时检测循环集合中的对象是否具有有效的运行时类型。它只检测由IEnumerator.Current返回来的System.Object是否可转换为循环中使用的对象类型,这个例子中是MyType类型。
最后,有些时候你想知道一个对象的精确类型,而不仅仅是满足当前可转换的目标类型。as运算符在为任何一个从目标类型上派生的对象进行转换时返回true。GetType()方法取得一个对象的运行时对象,它提供了比is和as更严格的(类型)测试。GetType()返回的类型可以与指定的类型进行比较(,从而知道它是不是我们想要的类型)。
再次考虑这个函数:
如果你想要写一个函数,使它对所有MyType类型的实例都能工作,上面的方法是非常不错的。如果你只想写一个函数,只对精确的MyType对象有效,那你必须用精确的类型比较。这里,你可以在foreach循环的内部完成。大多数时候,认为运行时确定对象的精确类型是很重要的,是在为对象做相等测试的时候。在大多数其它的比较中,由is和as提供的.isinst(译注:IL指令)比较,从语义上讲已经是正确的了。
好的面向对象实践告诉我们,你应该避免类型转换,但有些时候我没别无选择。当你无法避免转换时,用(C#)语言为我们提供的is和as运算符来清楚的表达你的意思吧。不同方法的强制转换有不同的规则。从语义上讲,is和as在绝大多转换上正确的,并当目标对象是正确的类型时, 它们总是成功的。应该选择这些基本指令来转换对象,至少它们会如你所期望的那样成功或者失败;而不是选择强制类型转换,这转换会产生一些意想不到的副作用。
25.来一个新技术WCF
26.呵呵,我就不贴了
实际上,微软和devxpress等公司都有公开的大堆的常用代码段
有兴趣可以去下devxpress的coderush(恩,好像现在叫DevExpress IDETools)
而微软的代码则在http://msdn.microsoft.com/zh-cn/vstudio/aa718338.aspx下载
使用代码段实际不只是知识点和小技巧,还有更现实的意义,代码段能够使你更方便快速的开发
27.刚刚写的通用删除数据方法
28..net自带的List <T>.Sort(Icompare)就是这样的复杂度,关于Icompare可以利用虚函数(委托)实现。建议搜一下我说的词。
如果搜不到,另外开贴,我给大家分享一下。
29.你们逼我发biao的,C#技巧,当然要这么写啦:
1.如果可能尽量使用接口来编程
注:这样可以提高代码稳定性!
2. 使用属性代替原始数据
注:代码会更灵活,利于封装!
3. 在Producer/Consumer 的Idiom中使用Delegate
注:这样做的好处是可以降低耦合!
4. 注意初始化顺序
这个我就不代码说明了,想想就清楚了呗,从上而下的设计原则,当然要注意.
30.累加:
从 1 加到十万……一行解决……
31.
1.使用resharper,尤其是使用其中的Refactory,来修改名称,做参数调整。。。等重构动作
2.把常用的代码段拉入左边的代码片段,下次使用的时候,直接将代码片段拖入即可。
32.不用临时变量交换两个int变量的值
33.try和catch块写在循环体的外面:
34.能提几个小问题吗?
1. 关于字段和属性,你大量的使用了字段,而非属性,当然,对程序不会有影响,但可能会影响到你的工资,哈哈。建议字段全部private,并公开一个属性去访问字段。
2. 关于try{}catch{}块,适当的使用try catch块能让程序更健壮,但看得出,你的这个类应该是一个公共类,是供其它类调用的,而你的catch却隐藏了所有的异常细节,换句话说,我传入一个完错误的sql,使用你的类去执行,只会得到一个false,却看不到任何有用的错误细节。非常不利于程序的调试。
3. 方法的命名规则,CloseRs()要关闭什么呢?SetTransaction()通常理解为对字段的赋值,却又没有参数,StartTransaction()会不会更好?(不要使用Begin开头的方法,除非这是个异步的方法)
4. 方法的返回值,没必要将每个方法的执行结果使用布尔值返回给用户,void就可以。而且,方法失败建议直接抛出异常,而不是告诉调用代码,“我挂了”,因为你抛出异常,人家就知道你“挂”了
5. 使用枚举提高代码的可用性,如DataType就可以设置成枚举类型。
就这些,纯属个人浅见,见笑。
35.空判断操作:
36.获取html源文件中所有a标记, 且分离出其中的href地址以及innerText 内容
+展开
-C#
object o = GetFromCache("A_KEY");
EmployeeInfo employee = o as EmployeeInfo;
if(employee != null) {
// TODO: 代码
}
EmployeeInfo employee = o as EmployeeInfo;
if(employee != null) {
// TODO: 代码
}
2. 使用DataReader读取数据
+展开
-C#
using(SqlDataReader reader = SqlHelper.ExecuteReader(cmd)) {
while(reader.read()) {
// TODO: 读取当前行的数据
}
}
while(reader.read()) {
// TODO: 读取当前行的数据
}
}
3. 尽量使用强类型集合(包括泛型集合),而非DataTable
+展开
-C#
using(SqlDataReader reader = SqlHelper.ExecuteReader(cmd)) {
Ilist<EmployeeInfo> list = new List<EmployeeInfo>();
while(reader.read()) {
list.add(new EmployeeInfo(
reader.getInt32(0)
// 其它字段
));
}
}
Ilist<EmployeeInfo> list = new List<EmployeeInfo>();
while(reader.read()) {
list.add(new EmployeeInfo(
reader.getInt32(0)
// 其它字段
));
}
}
4. 使用StringBuilder操作频繁变动的字符串,但以下情况例外
+展开
-C#
string s = "str1" + "str2" + "str3" + "str4"; // 这段代码不需要使用StringBuilder,因为编译后的代码为 string s = "str1str2str3str4";
5.刚给control写了个扩展方法,按回车发送tab键的 ^_^
+展开
-C#
public static class ControlExtensions
{
public static void SendTabKey(this Control control, Keys key)
{
if (key == Keys.Enter)
SendKeys.Send("{TAB}");
}
}
{
public static void SendTabKey(this Control control, Keys key)
{
if (key == Keys.Enter)
SendKeys.Send("{TAB}");
}
}
6.遍歷control
+展开
-C#
private void PanelClear(Control c)
{
foreach (Control cc in c.Controls)
{
if (cc.GetType() != typeof(Panel))
{
PanelClear(cc);
}
else
{
((Panel)cc).Visible = false;
}
}
}
{
foreach (Control cc in c.Controls)
{
if (cc.GetType() != typeof(Panel))
{
PanelClear(cc);
}
else
{
((Panel)cc).Visible = false;
}
}
}
7
1)对所有类都重写ToString()方法,这样在调试,绑定中都非常有用。
2)使用Enum代替奇迹数
8找出是否存在某個窗體FORM
+展开
-C#
efor (int j = 0; j < Application.OpenForms.Count; j++)
{
if (Application.OpenForms[j].Name.Equals("FNO31000"))
{
fno3100 = true;
}
}
If (fno3100 = true)
FNO31000 fno = (FNO31000)Application.OpenForms["FNO31000"]
{
if (Application.OpenForms[j].Name.Equals("FNO31000"))
{
fno3100 = true;
}
}
If (fno3100 = true)
FNO31000 fno = (FNO31000)Application.OpenForms["FNO31000"]
9.將datagridview的某個checkbox不顯示
+展开
-C#
public void chang_COLOR(int i, string str)//將顯示為N者不顯示選取方塊
{
dataGridView1.Rows[i].Cells[str].ReadOnly = true;
DataGridViewCell cell = new DataGridViewTextBoxCell();
cell.Style.BackColor = Color.Wheat;
//cell.ReadOnly = true;
cell.Value = "N";
cell.Style.BackColor = Color.White;
dataGridView1.Rows[i].Cells[str] = cell;
dataGridView1.Rows[i].Cells[str].Style.ForeColor = Color.White;
dataGridView1.Rows[i].Cells[str].Style.SelectionBackColor = Color.White;
dataGridView1.Rows[i].Cells[str].Style.SelectionForeColor = Color.White;
}
{
dataGridView1.Rows[i].Cells[str].ReadOnly = true;
DataGridViewCell cell = new DataGridViewTextBoxCell();
cell.Style.BackColor = Color.Wheat;
//cell.ReadOnly = true;
cell.Value = "N";
cell.Style.BackColor = Color.White;
dataGridView1.Rows[i].Cells[str] = cell;
dataGridView1.Rows[i].Cells[str].Style.ForeColor = Color.White;
dataGridView1.Rows[i].Cells[str].Style.SelectionBackColor = Color.White;
dataGridView1.Rows[i].Cells[str].Style.SelectionForeColor = Color.White;
}
10.打開某個路徑下的程序
+展开
-C#
Process p = new Process();
p.StartInfo.FileName = "cmd.exe"; //设定程序名
p.StartInfo.UseShellExecute = false; //关 ?Shell的使用
p.StartInfo.RedirectStandardInput = true; //重定向标 -?输 ¤J
p.StartInfo.RedirectStandardOutput = true; //重定向标 -?输 ¥X
p.StartInfo.RedirectStandardError = true; //重定向错 ?输 ¥X
p.StartInfo.CreateNoWindow = true; //设置不显 ¥ü窗口
p.StartInfo.WorkingDirectory = @"E:\";
p.Start(); //启 ?
p.StandardInput.WriteLine("新增文字文件.bat");
p.StandardInput.WriteLine("exit");
p.StartInfo.FileName = "cmd.exe"; //设定程序名
p.StartInfo.UseShellExecute = false; //关 ?Shell的使用
p.StartInfo.RedirectStandardInput = true; //重定向标 -?输 ¤J
p.StartInfo.RedirectStandardOutput = true; //重定向标 -?输 ¥X
p.StartInfo.RedirectStandardError = true; //重定向错 ?输 ¥X
p.StartInfo.CreateNoWindow = true; //设置不显 ¥ü窗口
p.StartInfo.WorkingDirectory = @"E:\";
p.Start(); //启 ?
p.StandardInput.WriteLine("新增文字文件.bat");
p.StandardInput.WriteLine("exit");
11.再来一些自认为比较高级的:
1). 字符串留用技术
字符串留用技术用来处理大量的字符串,而这些字符串中又会有许多重复的字符,例如:
"str1", "str2", ... , "str100" 共10,000个这样的字符,毫无疑问,其实就99个字符
这种情况可以使用到字符串留用技术,会提到性能
2). 数组永远是0基的
3). 使用多线程或异步操作的时候使用线程池的QueueUserWorkItem()方法将需要执行的任务排队,而不是手动去Start一个线程
12.
+展开
-C#
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
public delegate bool DelegateTest(object obj1, object obj2);
class Class1
{
static void Main()
{
Employee[] Employees =
{
new Employee("huguo",1000000),
new Employee("lili",20000),
new Employee("lulu",30000),
new Employee("xixi",50000),
new Employee("jianjian",10000),
new Employee("yoyo",9000)
};
//委托DelegateTest代理的方法是Greate
DelegateTest MyTest = new DelegateTest(Employee.Greate);
Sorter MySort = new Sorter();
//冒泡算法中第一个参数是对应Employees数组信息,第二个参数是委托
MySort.Sort(Employees, MyTest);
for (int m = 0; m < Employees.Length; m++)
{
Console.WriteLine(Employees[m].ToString());
}
}
}
class Employee
{
public string Name;
public int Salary;
public Employee(string Name, int Salary)
{
this.Name = Name;
this.Salary = Salary;
}
//用override重写string方法
public override string ToString()
{
return string.Format(Name + ",{0:C},", Salary);
}
//定义一个方法,如果obj2传过来的 Salary大于obj1就返回true;
public static bool Greate(object obj1, object obj2)
{
Employee Employee1 = (Employee)obj1;
Employee Employee2 = (Employee)obj2;
return (Employee2.Salary > Employee1.Salary) ? true : false;
}
}
class Sorter
{
public void Sort(object[] ArrayObj, DelegateTest Test)
{
//下面就是冒泡算法啦
for (int i = 0; i < ArrayObj.Length; i++)
{
for (int j = i + 1; j < ArrayObj.Length; j++)
{
if (Test(ArrayObj[j], ArrayObj[i]))
{
object Temp = ArrayObj[i];
ArrayObj[i] = ArrayObj[j];
ArrayObj[j] = Temp;
}
}
}
}
}
}
protected void Page_Load(object sender, EventArgs e)
{
if(!Page.IsPostBack)
{
CreateNum();
}
}
//在从1到20间随机生成6个互不相同的整数。
public void CreateNum()
{
ArrayList MyArray = new ArrayList();
Random random = new Random();
string str = null;
//循环的次数
int Nums = 6;
while (Nums > 0)
{
int i = random.Next(1, 9);
if (!MyArray.Contains(i))
{
if (MyArray.Count < 6)
{
MyArray.Add(i);
}
}
Nums -= 1;
}
for (int j = 0; j <= MyArray.Count - 1; j++)
{
str += MyArray[j].ToString();
}
Response.Write(str);
}
//方法1
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
ReplaceStr();
}
}
public void ReplaceStr()
{
ArrayList MyArray = new ArrayList();
MyArray.Add("123");
MyArray.Add("aaa");
if (MyArray.Contains("aaa"))
{
MyArray.Remove("aaa");
MyArray.Add("bbb");
}
for (int i = 0; i <= MyArray.Count - 1; i++)
{
Response.Write(MyArray[i].ToString());
}
}
//方法2
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
ReplaceStr();
}
}
public void ReplaceStr()
{
string [] tm = new string[] { "123", "aaa" };
for (int i = 0; i < tm.Length; i++)
{
if (tm[i].ToString() == "aaa")
{
tm[i]=tm[i].Replace("aaa", "bbb");
}
}
for (int i = 0; i <= tm.Length - 1; i++)
{
Response.Write(tm[i].ToString());
}
}
//方法3
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
string[] tm = new string[] { "123", "aaa" };
string Array=ReplaceValue(tm, "aaa", "bbb", ",");
Response.Write(Array);
}
}
public static string ReplaceValue(string[] item, string oldSv, string newSv, string separator)
{
if (item == null) return string.Empty;
StringBuilder sb = new StringBuilder();
foreach (string s in item)
{
if (s == oldSv)
{
sb.Append(newSv);
sb.Append(separator);
}
else
{
sb.Append(s);
sb.Append(separator);
}
}
string returnstr = sb.ToString();
returnstr = (returnstr.EndsWith(separator)) ? returnstr.Substring(0, returnstr.Length - 1) : returnstr;
return returnstr;
13.
private static void PrepareCommand(MySqlConnection con,MySqlCommand mycmd,string cmdText,MySqlParameter[] cmdParams,CommandType cmdType)
{
con.Open();
mycmd.Connection = con;
mycmd.CommandText = cmdText;
mycmd.CommandType = cmdType;
if (cmdParams != null)
{
foreach (MySqlParameter param in cmdParams)
{
//if((param.Direction==ParameterDirection.InputOutput || param.Direction==ParameterDirection.Input) && (param.Value==null))
//{
// param.Value = DBNull.Value;
mycmd.Parameters.Add(param);
//}
}
}
}
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
public delegate bool DelegateTest(object obj1, object obj2);
class Class1
{
static void Main()
{
Employee[] Employees =
{
new Employee("huguo",1000000),
new Employee("lili",20000),
new Employee("lulu",30000),
new Employee("xixi",50000),
new Employee("jianjian",10000),
new Employee("yoyo",9000)
};
//委托DelegateTest代理的方法是Greate
DelegateTest MyTest = new DelegateTest(Employee.Greate);
Sorter MySort = new Sorter();
//冒泡算法中第一个参数是对应Employees数组信息,第二个参数是委托
MySort.Sort(Employees, MyTest);
for (int m = 0; m < Employees.Length; m++)
{
Console.WriteLine(Employees[m].ToString());
}
}
}
class Employee
{
public string Name;
public int Salary;
public Employee(string Name, int Salary)
{
this.Name = Name;
this.Salary = Salary;
}
//用override重写string方法
public override string ToString()
{
return string.Format(Name + ",{0:C},", Salary);
}
//定义一个方法,如果obj2传过来的 Salary大于obj1就返回true;
public static bool Greate(object obj1, object obj2)
{
Employee Employee1 = (Employee)obj1;
Employee Employee2 = (Employee)obj2;
return (Employee2.Salary > Employee1.Salary) ? true : false;
}
}
class Sorter
{
public void Sort(object[] ArrayObj, DelegateTest Test)
{
//下面就是冒泡算法啦
for (int i = 0; i < ArrayObj.Length; i++)
{
for (int j = i + 1; j < ArrayObj.Length; j++)
{
if (Test(ArrayObj[j], ArrayObj[i]))
{
object Temp = ArrayObj[i];
ArrayObj[i] = ArrayObj[j];
ArrayObj[j] = Temp;
}
}
}
}
}
}
protected void Page_Load(object sender, EventArgs e)
{
if(!Page.IsPostBack)
{
CreateNum();
}
}
//在从1到20间随机生成6个互不相同的整数。
public void CreateNum()
{
ArrayList MyArray = new ArrayList();
Random random = new Random();
string str = null;
//循环的次数
int Nums = 6;
while (Nums > 0)
{
int i = random.Next(1, 9);
if (!MyArray.Contains(i))
{
if (MyArray.Count < 6)
{
MyArray.Add(i);
}
}
Nums -= 1;
}
for (int j = 0; j <= MyArray.Count - 1; j++)
{
str += MyArray[j].ToString();
}
Response.Write(str);
}
//方法1
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
ReplaceStr();
}
}
public void ReplaceStr()
{
ArrayList MyArray = new ArrayList();
MyArray.Add("123");
MyArray.Add("aaa");
if (MyArray.Contains("aaa"))
{
MyArray.Remove("aaa");
MyArray.Add("bbb");
}
for (int i = 0; i <= MyArray.Count - 1; i++)
{
Response.Write(MyArray[i].ToString());
}
}
//方法2
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
ReplaceStr();
}
}
public void ReplaceStr()
{
string [] tm = new string[] { "123", "aaa" };
for (int i = 0; i < tm.Length; i++)
{
if (tm[i].ToString() == "aaa")
{
tm[i]=tm[i].Replace("aaa", "bbb");
}
}
for (int i = 0; i <= tm.Length - 1; i++)
{
Response.Write(tm[i].ToString());
}
}
//方法3
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
string[] tm = new string[] { "123", "aaa" };
string Array=ReplaceValue(tm, "aaa", "bbb", ",");
Response.Write(Array);
}
}
public static string ReplaceValue(string[] item, string oldSv, string newSv, string separator)
{
if (item == null) return string.Empty;
StringBuilder sb = new StringBuilder();
foreach (string s in item)
{
if (s == oldSv)
{
sb.Append(newSv);
sb.Append(separator);
}
else
{
sb.Append(s);
sb.Append(separator);
}
}
string returnstr = sb.ToString();
returnstr = (returnstr.EndsWith(separator)) ? returnstr.Substring(0, returnstr.Length - 1) : returnstr;
return returnstr;
13.
private static void PrepareCommand(MySqlConnection con,MySqlCommand mycmd,string cmdText,MySqlParameter[] cmdParams,CommandType cmdType)
{
con.Open();
mycmd.Connection = con;
mycmd.CommandText = cmdText;
mycmd.CommandType = cmdType;
if (cmdParams != null)
{
foreach (MySqlParameter param in cmdParams)
{
//if((param.Direction==ParameterDirection.InputOutput || param.Direction==ParameterDirection.Input) && (param.Value==null))
//{
// param.Value = DBNull.Value;
mycmd.Parameters.Add(param);
//}
}
}
}
14.在写公共类库,使用catch时,最好不要catch(Exception e){throw e;}而使用catch{throw;},同时如果使用catch捕捉异常,最好使用catch(最具体的Exception e){...}catch(最具体的Exception e){...}...一直到所有确认的错误都能检查到 ;而不要使用catch(基类Exception e){...}
15.推荐使用using(){}
去写实现了IDisposable的对象
非常漂亮!
16.如果实现了IDisposable的对象在访问完成后要进行关闭,则在try{...}finally{...//关闭代码},或直接调用using(...){...}。
17.
+展开
-C#
//尽量不用
string str="";
//而是
string str=string.Empty;
string str="";
//而是
string str=string.Empty;
18.Effective C#
原则5:始终提供ToString()(部分翻译)
Always Provide ToString()
在.Net世界里,用得最多的方法之一就是System.Object.ToStrying()了。你应该为你所有的客户写一个“通情达理”的类(译注:这里是指这个类应该对用户友好)。要么,你就迫使所用类的用户,去使用类的属性并添加一些合理的易读的说明。这个以字符串形式存在,关于你设计的类的说明,可以很容易的向你的用户显示一些关于对象的信息到:Windows Form里,Web Form里,控制台输出。这些字符说明可以用于调试。你写的任何一种类型,都应该合理的重写这个方法。当你设计更多的复杂的类型时,你应该实现应变能力更强的IFormattable.ToString(). 承认这个:如果你不重写(override)这个常规的方法,或者只是写一个很糟糕的,你的客户将不得不为你修正它。
System.Object版的ToString()方法只返回类型的名字。这并没有太多有用的信息:“Rect”,“Point”,“Size”并不会如你所想的那样显示给你的用户。但那只是在你没有为你的类重写ToString()方法时得到的。你只用为你的类写一次,但你的客户却会使用很多次。当你设计一个类时,多添加一点小小的工作,就可以在你或者是其他人每次使用时得到回报。
(译注:废话!)
======================
这一原则就不翻译了,看的有点郁闷。就是ToString()的几个重写版本。以及一些格式化输出。我觉得本书不应该讨论这些入门级的内容,所以只是读了一遍,就没有全部翻译。
大家知道要重写它就行了,最好是提供几个重载版本。回头有时间再翻译这一原则的其它内容。
给一点个人建议,一般不会在一个类的ToString上提供很多的说明,给一个名字就已经足够了,然后加一个SDK帮助。更多时候,在后面添加成员类的说明。我就在一个第三方库的ToString上看到很严谨的结构,都是在类名后面,添加一些内容和重要属性的说明。
=========================================补译:
让我们来考虑一个简单的需求:重写System.Object.ToString()方法。你所设计的每一个类型都应该重写ToString()方法,用来为你的类型提供一些最常用的文字说明。考虑这个Customer类以及它的三个成员(fields)(译注:一般情况,类里的fields译为成员,这是面向对象设计时的概念,而在与数据库相关的地方,则是指字段):
+展开
-C#
public class Customer
{
private string _name;
private decimal _revenue;
private string _contactPhone;
}
{
private string _name;
private decimal _revenue;
private string _contactPhone;
}
默认继承自System.Object的ToString()方法会返回"Customer"。这对每个人都不会有太大的帮助。就算ToString()只是为了在调试时使用,也应该更灵活(sophisticated)一些。你重写的ToString()方法应该返回文字说明,更像是你的用户在使用这个类一样。在Customer例子中,这应该是名字:
+展开
-C#
public override string ToString()
{
return _name;
}
{
return _name;
}
如果你不遵守这一原则里的其它意见,就按照上面的方法为你所定义的所有类型重写该方法。它会直接为每个人省下时间。
当你负责任的为Object.ToString()方法实现了重写时,这个类的对象可以更容易的被添加到Windows Form里,Web Form里,或者打印输出。 .NET的FCL使用重载的Object.ToString()在控件中显示对象:组合框,列表框,文本框,以及其它一些控件。如果你一个Windows Form或者Web Form里添加一个Customer对象的链表,你将会得到它们的名字(以文本)显示出来(译注:而不是每个对象都是同样的类型名)。
Syste.Console.WriteLine()和System.String.Formate()在内部(实现的方法)是一样的。任何时候,.Net的FCL想取得一个customer的字符串说明时,你的customer类型会提供一个客户的名字。一个只有三行的简单函数,完成了所有的基本需求。
这是一个简单的方法,ToString()还可以以文字(输出的方法)满足很多用户自定义类型的需求。但有些时候,你的要求可能会更多。前面的customer类型有三个成员:名字,收入和联系电话。对System.Object.ToString()(译注:原文这里有误,掉了Object)的重写只使用了_name。你可以通过实现IFormattable(这个接口)来弥补这个不足。这是一个当你需要对外输出格式化文本时使用的接口。IFormattable包含一个重载版的ToString()方法,使用这个方法,你可以为你的类型信息指定详细的格式。这也是一个当你要产生并输出多种格式的字符串时要使用的接口。customer类就是这种情况,用户将希望产生一个报表,这个报表包含了已经表格化了的用户名和去年的收入。IFormattable.ToString()方法正合你意,它可以让用户格式化输出你的类型信息。这个方法原型的参数上一包含一个格式化字符串和一个格式化引擎:
+展开
-C#
string System.IFormattable.ToString( string format,
IFormatProvider formatProvider )
IFormatProvider formatProvider )
你可以为你设计的类型指定要使用的格式字符串。你也可以为你的格式字符串指定关键字符。在这个customer的例子中,你可以完全可以用n来表示名字,r表示收入以及p来表示电话。这样一来,你的用户就可以随意的组合指定信息,而你则须要为你的类型提供下面这个版本的的IFormattable.ToString():
+展开
(译注:上面的做法显然不合理,要是我的对象有10个成员,这样的组合是会让人疯掉的。推荐使用正则表达式来完成这样的工作,正则表达式在处理文字时的表现还是很出色的。) -C#
#region IFormattable Members
// supported formats:
// substitute n for name.
// substitute r for revenue
// substitute p for contact phone.
// Combos are supported: nr, np, npr, etc
// "G" is general.
string System.IFormattable.ToString( string format,
IFormatProvider formatProvider )
{
if ( formatProvider != null )
{
ICustomFormatter fmt = formatProvider.GetFormat(
this.GetType( ) )
as ICustomFormatter;
if ( fmt != null )
return fmt.Format( format, this, formatProvider );
}
switch ( format )
{
case "r":
return _revenue.ToString( );
case "p":
return _contactPhone;
case "nr":
return string.Format( "{0,20}, {1,10:C}",
_name, _revenue );
case "np":
return string.Format( "{0,20}, {1,15}",
_name, _contactPhone );
case "pr":
return string.Format( "{0,15}, {1,10:C}",
_contactPhone, _revenue );
case "pn":
return string.Format( "{0,15}, {1,20}",
_contactPhone, _name );
case "rn":
return string.Format( "{0,10:C}, {1,20}",
_revenue, _name );
case "rp":
return string.Format( "{0,10:C}, {1,20}",
_revenue, _contactPhone );
case "nrp":
return string.Format( "{0,20}, {1,10:C}, {2,15}",
_name, _revenue, _contactPhone );
case "npr":
return string.Format( "{0,20}, {1,15}, {2,10:C}",
_name, _contactPhone, _revenue );
case "pnr":
return string.Format( "{0,15}, {1,20}, {2,10:C}",
_contactPhone, _name, _revenue );
case "prn":
return string.Format( "{0,15}, {1,10:C}, {2,15}",
_contactPhone, _revenue, _name );
case "rpn":
return string.Format( "{0,10:C}, {1,15}, {2,20}",
_revenue, _contactPhone, _name );
case "rnp":
return string.Format( "{0,10:C}, {1,20}, {2,15}",
_revenue, _name, _contactPhone );
case "n":
case "G":
default:
return _name;
}
}
#endregion
// supported formats:
// substitute n for name.
// substitute r for revenue
// substitute p for contact phone.
// Combos are supported: nr, np, npr, etc
// "G" is general.
string System.IFormattable.ToString( string format,
IFormatProvider formatProvider )
{
if ( formatProvider != null )
{
ICustomFormatter fmt = formatProvider.GetFormat(
this.GetType( ) )
as ICustomFormatter;
if ( fmt != null )
return fmt.Format( format, this, formatProvider );
}
switch ( format )
{
case "r":
return _revenue.ToString( );
case "p":
return _contactPhone;
case "nr":
return string.Format( "{0,20}, {1,10:C}",
_name, _revenue );
case "np":
return string.Format( "{0,20}, {1,15}",
_name, _contactPhone );
case "pr":
return string.Format( "{0,15}, {1,10:C}",
_contactPhone, _revenue );
case "pn":
return string.Format( "{0,15}, {1,20}",
_contactPhone, _name );
case "rn":
return string.Format( "{0,10:C}, {1,20}",
_revenue, _name );
case "rp":
return string.Format( "{0,10:C}, {1,20}",
_revenue, _contactPhone );
case "nrp":
return string.Format( "{0,20}, {1,10:C}, {2,15}",
_name, _revenue, _contactPhone );
case "npr":
return string.Format( "{0,20}, {1,15}, {2,10:C}",
_name, _contactPhone, _revenue );
case "pnr":
return string.Format( "{0,15}, {1,20}, {2,10:C}",
_contactPhone, _name, _revenue );
case "prn":
return string.Format( "{0,15}, {1,10:C}, {2,15}",
_contactPhone, _revenue, _name );
case "rpn":
return string.Format( "{0,10:C}, {1,15}, {2,20}",
_revenue, _contactPhone, _name );
case "rnp":
return string.Format( "{0,10:C}, {1,20}, {2,15}",
_revenue, _name, _contactPhone );
case "n":
case "G":
default:
return _name;
}
}
#endregion
添加了这样的函数后,你就让用户具有了可以这样指定customer数据的能力:
+展开
-C#
IFormattable c1 = new Customer();
Console.WriteLine( "Customer record: {0}",
c1.ToString( "nrp", null ) );
Console.WriteLine( "Customer record: {0}",
c1.ToString( "nrp", null ) );
任何对IFormattable.ToString()的实现都要指明类型,但不管你在什么时候实现IFormattation接口,你都要注意处理大小写。首先,你必须支持能用格式化字符:“G”。其次,你必须支持两个空格式化字符:""和null。当你重载Object.ToString()这个方法时,这三个格式化字符应该返回同样的字符串。.Net的FCL经常用null来调用IFormattable.ToString()方法,来取代对Object.ToString()的调用,但在少数地方使用格式符"G"来格式化字符串,从而区别通用的格式。如果你添加了对IFormattable接口的支持,并不再支持标准的格式化,你将会破坏FCL里的字符串的自动(隐式)转换。
IFormattable.ToString()的第二个参数是一个实现了IFormatProvider接口的对象。这个对象为用户提供了一些你没有预先设置的格式化选项(译注:简单一点,就是你可以只实现你自己的格式化选项,其它的默认由它来完成)。如果你查看一下前面IFormattable.ToString()的实现,你就会毫不犹豫的拿出不计其数的,任何你喜欢的格式化选项,而这些都是的格式化中所没有的。支持人们容易阅读的输出是很自然的事,但不管你支持多少种格式,你的用户总有一天会想要你预先没想到的格式。这就为什么这个方法的前几行要检察实现了IFormatProvider的对象,并把ICustomFormatter的工作委托给它了。
让我们把(讨论的)焦点从类的作者转移到类的使用者上来。你发现你想要的格式化不被支持。例如,你有一个一组客户,他们的名字有的大于20个字符,并且你想修改格式化选项,让它支持50个字符长的客户名。这就是为什么IFormatProvider接口要存在。你可以设计一个实现了IFormatProvider的类,并且让它同时实现ICustomFormatter接口用于格式化输出。IFormatProvider接口定义了一个方法:GetFormat()。这个方法返回一个实现了ICustomFormatter接口的对象。由ICustomFormatter接口的指定方法来完成实际的格式化工作。下面这一对(接口)实现了对输出的修改,让它可以支持50个字符长的用户名:
+展开
-C#
// Example IFormatProvider:
public class CustomFormatter : IFormatProvider
{
#region IFormatProvider Members
// IFormatProvider contains one method.
// This method returns an object that
// formats using the requested interface.
// Typically, only the ICustomFormatter
// is implemented
public object GetFormat( Type formatType )
{
if ( formatType == typeof( ICustomFormatter ))
return new CustomerFormatProvider( );
return null;
}
#endregion
// Nested class to provide the
// custom formatting for the Customer class.
private class CustomerFormatProvider : ICustomFormatter
{
#region ICustomFormatter Members
public string Format( string format, object arg,
IFormatProvider formatProvider )
{
Customer c = arg as Customer;
if ( c == null )
return arg.ToString( );
return string.Format( "{0,50}, {1,15}, {2,10:C}",
c.Name, c.ContactPhone, c.Revenue );
}
#endregion
}
}
public class CustomFormatter : IFormatProvider
{
#region IFormatProvider Members
// IFormatProvider contains one method.
// This method returns an object that
// formats using the requested interface.
// Typically, only the ICustomFormatter
// is implemented
public object GetFormat( Type formatType )
{
if ( formatType == typeof( ICustomFormatter ))
return new CustomerFormatProvider( );
return null;
}
#endregion
// Nested class to provide the
// custom formatting for the Customer class.
private class CustomerFormatProvider : ICustomFormatter
{
#region ICustomFormatter Members
public string Format( string format, object arg,
IFormatProvider formatProvider )
{
Customer c = arg as Customer;
if ( c == null )
return arg.ToString( );
return string.Format( "{0,50}, {1,15}, {2,10:C}",
c.Name, c.ContactPhone, c.Revenue );
}
#endregion
}
}
GetFormat()方法取得一个实现了ICustomFormatter接口的对象。而ICustomFormatter.Format()方法,则根据用户需求负责实际的格式化输出工作。这个方法把对象转换成格式化的字符串。你可以为ICustomFormatter.Format()定义格式化字符串,因此你可以按常规指定多重格式。FormatProvider就是一个由GetFormat()方法取得的IFormatProvider对象。
为了满足用户的格式化要求,你必须用IFormatProvider对象明确的调用string.Format()方法:
Console.WriteLine( string.Format( new CustomFormatter(), "", c1 ));
你可以设计一个类,让它实现IFormatProvider和ICustomFormatter接口,再实现或者不实现IFormattable 接口。因此,即使这个类的作者没有提供合理的ToStrying行为,你可以自己来完成。当然,从类的外面来实现,你只能访问公共属性成数据来取得字符串。实现两个接口,IFormatProvider 和 IcustomFormatter, 只做一些文字输出,并不需要很多工作。但在.Net框架里,你所实现的指定的文字输出在哪里都可以得到很好的支持。
所以,再回到类的作者上来。重写Object.ToString(),为你的类提供一些说明是件很简单的事。你每次都应该为你的类型提供这样的支持。而且这应该是对你的类型最显而易见的,最常用的说明。在一些极端情况下,你的格式化不能支持一些过于灵活的输出时,你应该借用IFormattable接口的优势。它为你的类型进行自定义格式化输出提供了标准方法。如果你放弃这些,你的用户将失去用于实现自定义格式化的工具。这些解决办法须要写更多的代码,并且因为你的用户是在类的外面的,所以他们无法检查类的里面的状态。
最后,大家注意到你的类型的信息,他们会明白输出的文字。尽可能以简单的方式的提供这样的信息吧:为你的所有类型重写ToString()方法。
18.条款1:使用属性代替可访问的数据成员
条款2:运行时常量(readonly)优于编译时常量(const)
条款3:操作符is或as优于强制转型
条款4:使用Conditional特性代替#if条件编译
条款5:总是提供ToString()方法
条款6:明辨值类型和引用类型的使用场合
条款7:将值类型尽可能实现为具有常量性和原子性的类型
条款8:确保0为值类型的有效状态
条款9:理解几个相等判断之间的关系
条款10:理解GetHashCode()方法的缺陷
条款11:优先采用foreach循环语句
条款12:变量初始化器优于赋值语句
条款13:使用静态构造器初始化静态类成员
条款14:利用构造器链
条款15:利用using和try/finally语句来清理资源
条款16:尽量减少内存垃圾
条款17:尽量减少装箱与拆箱
条款18:实现标准Dispose模式
条款19:定义并实现接口优于继承类型
条款20:明辨接口实现和虚方法重写
条款21:使用委托表达回调
条款22:使用事件定义外发接口
条款23:避免返回内部类对象的引用
条款24:声明式编程优于命令式编程
条款25:尽可能将类型实现为可序列化的类型
条款26:使用IComparable和IComparer接口实现排序关系
条款27:避免ICloneable接口
条款28:避免强制转换操作符
条款29:只有当新版基类导致问题时才考虑使用new修饰符
条款30:尽可能实现CLS兼容的程序集
条款31:尽可能实现短小简洁的函数
条款32:尽可能实现小尺寸、高内聚的程序集
条款33:限制类型的可见性
条款34:创建大粒度的Web API
条款35:重写优于事件处理器
条款36:合理使用.NET运行时诊断
条款37:使用标准配置机制
条款38:定制和支持数据绑定
条款39:使用.NET验证
条款40:根据需要选用恰当的集合
条款41:DataSet优于自定义结构
条款42:利用特性简化反射
条款43:避免过度使用反射
条款44:为应用程序创建特定的异常类
条款45:优先选择强异常安全保证
条款46:最小化互操作
条款47:优先选择安全代码
条款48:掌握相关工具与资源
条款49:为C# 2.0做准备
条款50:了解ECMA标准
19.使用数据库时,存储过程参数,尽量不要拼接字符串。
数据库要建立索引、使用事务。
ASP.NET合理利用母版页和缓存。
提取字符串注意正则表达式的优化,提高准确性来减少迭代。
算术表达式要多用小括号。
private bool test = false;if(test){;}
结构简单时使用三目运算符。
灵活使用switch。
20.尽量使用托管代码。
平时写的代码要OOP,然后封装成自己的类库,提高日后开发效率。
多用组合,少用继承。
字符串合理使用Trim()。
实现文件复制时,尽量减少对磁盘读写次数。
21.换行字符串,相当于"\r\n",Environment.NewLine
contains代替indexof
字符串组合使用StringBuilder,减少字符串拷贝
使用接口来编程
使用属性代替原始数据
22.先通过
System.Diagnostics.Process类获取想要编辑的进程
调用API
+展开
[Flags]
public enum ProcessAccessType
{
PROCESS_TERMINATE = (0x0001),
PROCESS_CREATE_THREAD = (0x0002),
PROCESS_SET_SESSIONID = (0x0004),
PROCESS_VM_OPERATION = (0x0008),
PROCESS_VM_READ = (0x0010),
PROCESS_VM_WRITE = (0x0020),
PROCESS_DUP_HANDLE = (0x0040),
PROCESS_CREATE_PROCESS = (0x0080),
PROCESS_SET_QUOTA = (0x0100),
PROCESS_SET_INFORMATION = (0x0200),
PROCESS_QUERY_INFORMATION = (0x0400)
}
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);
[DllImport("kernel32.dll")]
public static extern Int32 CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
public static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
public static extern Int32 WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesWritten);
-C#
[Flags]
public enum ProcessAccessType
{
PROCESS_TERMINATE = (0x0001),
PROCESS_CREATE_THREAD = (0x0002),
PROCESS_SET_SESSIONID = (0x0004),
PROCESS_VM_OPERATION = (0x0008),
PROCESS_VM_READ = (0x0010),
PROCESS_VM_WRITE = (0x0020),
PROCESS_DUP_HANDLE = (0x0040),
PROCESS_CREATE_PROCESS = (0x0080),
PROCESS_SET_QUOTA = (0x0100),
PROCESS_SET_INFORMATION = (0x0200),
PROCESS_QUERY_INFORMATION = (0x0400)
}
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);
[DllImport("kernel32.dll")]
public static extern Int32 CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
public static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
public static extern Int32 WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesWritten);
打开进程
+展开
-C#
private IntPtr m_hProcess = IntPtr.Zero; //这个保存打开了个进程句柄
public void OpenProcess()
{
// m_hProcess = ProcessMemoryReaderApi.OpenProcess(ProcessMemoryReaderApi.PROCESS_VM_READ, 1, (uint)m_ReadProcess.Id);
ProcessMemoryReaderApi.ProcessAccessType access;
access = ProcessMemoryReaderApi.ProcessAccessType.PROCESS_VM_READ
| ProcessMemoryReaderApi.ProcessAccessType.PROCESS_VM_WRITE
| ProcessMemoryReaderApi.ProcessAccessType.PROCESS_VM_OPERATION;
m_hProcess = ProcessMemoryReaderApi.OpenProcess((uint)access, 1, (uint)m_ReadProcess.Id);
}
public void OpenProcess()
{
// m_hProcess = ProcessMemoryReaderApi.OpenProcess(ProcessMemoryReaderApi.PROCESS_VM_READ, 1, (uint)m_ReadProcess.Id);
ProcessMemoryReaderApi.ProcessAccessType access;
access = ProcessMemoryReaderApi.ProcessAccessType.PROCESS_VM_READ
| ProcessMemoryReaderApi.ProcessAccessType.PROCESS_VM_WRITE
| ProcessMemoryReaderApi.ProcessAccessType.PROCESS_VM_OPERATION;
m_hProcess = ProcessMemoryReaderApi.OpenProcess((uint)access, 1, (uint)m_ReadProcess.Id);
}
m_ReadProcess.Id 进程的ID编号
读取
+展开
-C#
public byte[] ReadProcessMemory(IntPtr MemoryAddress, uint bytesToRead, out int bytesRead)
{
byte[] buffer = new byte[bytesToRead];
IntPtr ptrBytesRead;
ProcessMemoryReaderApi.ReadProcessMemory(m_hProcess, MemoryAddress, buffer, bytesToRead, out ptrBytesRead);
bytesRead = ptrBytesRead.ToInt32();
return buffer;
}
{
byte[] buffer = new byte[bytesToRead];
IntPtr ptrBytesRead;
ProcessMemoryReaderApi.ReadProcessMemory(m_hProcess, MemoryAddress, buffer, bytesToRead, out ptrBytesRead);
bytesRead = ptrBytesRead.ToInt32();
return buffer;
}
IntPrt MemoryAddress 为要读取的内存地址
uint bytesToRead 需要读的数量
out int bytesRead 实际读出的数量
写入
+展开
-C#
public void WriteProcessMemory(IntPtr MemoryAddress, byte[] bytesToWrite, out int bytesWritten)
{
IntPtr ptrBytesWritten;
ProcessMemoryReaderApi.WriteProcessMemory(m_hProcess, MemoryAddress, bytesToWrite, (uint)bytesToWrite.Length, out ptrBytesWritten);
bytesWritten = ptrBytesWritten.ToInt32();
}
IntPrt MemoryAddress 为要读取的内存地址
byte[] bytesToWrite 需要写入的数据
out int bytesWritten 实际写入多少
23./// <summary>
///DataGriderView 自动排序
/// </summary>
/// <param name="sender"> </param>
/// <param name="e"> </param>
protected void GVData_Sorting(object sender, GridViewSortEventArgs e)
{
try
{
// 从事件参数获取排序数据列
string sortExpression = e.SortExpression.ToString();
// 假定为排序方向为“顺序”TechWeb-技术社区
string sortDirection = "ASC";
// “ASC”与事件参数获取到的排序方向进行比较,进行GridView排序方向参数的修改#p&V4u#o4C
if (sortExpression == this.GVData.Attributes["SortExpression"])
//获得下一次的排序状态
sortDirection = (this.GVData.Attributes["SortDirection"].ToString() == sortDirection ? "DESC" : "ASC");
// 重新设定GridView排序数据列及排序方向
this.GVData.Attributes["SortExpression"] = sortExpression;
this.GVData.Attributes["SortDirection"] = sortDirection;
}
catch (Exception ex)
{
MessageBox(ex.Message);
}
}
/// <summary>
/// 错误消息提示
/// </summary>
/// <param name="messageStr"> </param>
public void MessageBox(string messageStr)
{
Response.Write(" <script>alert('" + messageStr + "') </script>");
}
{
IntPtr ptrBytesWritten;
ProcessMemoryReaderApi.WriteProcessMemory(m_hProcess, MemoryAddress, bytesToWrite, (uint)bytesToWrite.Length, out ptrBytesWritten);
bytesWritten = ptrBytesWritten.ToInt32();
}
IntPrt MemoryAddress 为要读取的内存地址
byte[] bytesToWrite 需要写入的数据
out int bytesWritten 实际写入多少
23./// <summary>
///DataGriderView 自动排序
/// </summary>
/// <param name="sender"> </param>
/// <param name="e"> </param>
protected void GVData_Sorting(object sender, GridViewSortEventArgs e)
{
try
{
// 从事件参数获取排序数据列
string sortExpression = e.SortExpression.ToString();
// 假定为排序方向为“顺序”TechWeb-技术社区
string sortDirection = "ASC";
// “ASC”与事件参数获取到的排序方向进行比较,进行GridView排序方向参数的修改#p&V4u#o4C
if (sortExpression == this.GVData.Attributes["SortExpression"])
//获得下一次的排序状态
sortDirection = (this.GVData.Attributes["SortDirection"].ToString() == sortDirection ? "DESC" : "ASC");
// 重新设定GridView排序数据列及排序方向
this.GVData.Attributes["SortExpression"] = sortExpression;
this.GVData.Attributes["SortDirection"] = sortDirection;
}
catch (Exception ex)
{
MessageBox(ex.Message);
}
}
/// <summary>
/// 错误消息提示
/// </summary>
/// <param name="messageStr"> </param>
public void MessageBox(string messageStr)
{
Response.Write(" <script>alert('" + messageStr + "') </script>");
}
24.
选择is或者as操作符而不是做强制类型转换
C#是一个强数据类型语言。好的编程实践意味着当可以避免从一种数据类型强制转化为另种数据类型时,我们应该尽我们的所能来避免它。但在某些时候,运行时类型检测是不可避免的。在C#里,大多数时候你要为调用函数的参数使用System.Object类型,因为Framwork已经为我们定义了函数的原型。你很可能要试图把那些类型进行向下转化为其它类型的接口或者类。你有两个选择:用as运算符,或者,采用旧式的C风格,强制转换。(不管是哪一种,)你还必须对变量进行保护:你可以试着用is进行转换,然而再用as进行转换或者强制转换。
无论何时,正确的选择是用as运算符进行类型转换。因为比起盲目的强制转换它更安全,而且在运行时效率更高。用as和is运算符进行转换时,并不是对所有的用户定义的类型都能完成的。它们只在运行时类型和目标类型匹配的时候,转换才能成功。它们决不会构造一个新的对象来满足(转化)要求。
看一个例子。你写了一段代码,要转换一个任意类型的对象实例到一个MyType类型的实例。你是这样写代码的:
+展开
-C#
object o = Factory.GetObject( );
// Version one:
MyType t = o as MyType;
if ( t != null )
{
// work with t, it's a MyType.
} else
{
// report the failure.
}
// Version one:
MyType t = o as MyType;
if ( t != null )
{
// work with t, it's a MyType.
} else
{
// report the failure.
}
或者你这样写:
+展开
-C#
object o = Factory.GetObject( );
// Version two:
try {
MyType t;
t = ( MyType ) o;
if ( t != null )
{
// work with T, it's a MyType.
} else
{
// Report a null reference failure.
}
} catch
{
// report the conversion failure.
}
// Version two:
try {
MyType t;
t = ( MyType ) o;
if ( t != null )
{
// work with T, it's a MyType.
} else
{
// Report a null reference failure.
}
} catch
{
// report the conversion failure.
}
你会同意第一种写法更简单更容易读。它没有try/catch结构,所以你可以同时避免(性能)开销和(多写)代码。我们注意到,强制转换的方法为了检测转换是否把一个null的对象进行强制转换,而不得不添加一个捕获异常的结构。null可以被转换为任意的引用类型,但as操作符就算是转化一个null的引用时,也会(安全的)返回一个null。所以当你用强制类型转换时,就得用一个try/catch结构来捕获转换null时的异常。用as进行转换的时就,就只用简单的检测一下转化后的实例不为null就行了。
(译注:被转换对象和转换后的结果都有可能为null,上面就是对这两种null进行了说明,注意区分。强制转换是不安全的,可能会有异常抛出,因此要用try/catch结构来保证程序正常运行,而as转换是安全的,不会有异常抛出,但在转换失败后,其结果为null)
强制转换与as转换最大的区别表现在如何对待用户定义类型的转换。
与其它运算不一样,as和is运算符在运行时要检测转换目标的类型。如果一个指定对象不是要求转换的类型,或者它是从要求转换类型那里派生的,转换会失败。另一方面,强制转换可以用转换操作把一个对象转换成要求的类型。这还包括对内置数据(built-in numberic)类型的转换。强制转换一个long到一个short可能会丢失数据。
同样的问题也隐藏在对用户定义类型的转换上。考虑这样的类型:
+展开
-C#
public class SecondType
{
private MyType _value;
// other details elided
// Conversion operator.
// This converts a SecondType to
// a MyType, see item 29.
public static implicit operator MyType( SecondType t )
{
return t._value;
}
}
{
private MyType _value;
// other details elided
// Conversion operator.
// This converts a SecondType to
// a MyType, see item 29.
public static implicit operator MyType( SecondType t )
{
return t._value;
}
}
假设代码片段中开始的Factory.GetObject()函数返回的是SecondType 类型的数据:
+展开
两种转换都失败了。但是我告诉过你,强制转化可以在用户定义的类型上完成。你应该想到强制转化会成功。你是对的--(如果)它们跟像你想的一样是会成功的。但是转换失败了,因为你的编译器为对象o产生的代码是基于编译时类型。而对于运行时对象o,编译器什么也不知道,它们被视为System.Obejct类型。编译器认为,不存在System.Object类型到用户类型MyType的转换。它检测了System.Object和MyType的定义。缺少任意的用户定义类型转换,编译器(为我们)生成了用于检测运行时对象o的代码,并且检测它是不是MyType类型。因为对象o是SecondType类型,所以失败了。编译器并不去检测实际运行时对象o是否可以被转换为MyType类型。 -C#
object o = Factory.GetObject( );
// o is a SecondType:
MyType t = o as MyType; // Fails. o is not MyType
if ( t != null )
{
// work with t, it's a MyType.
} else
{
// report the failure.
}
// Version two:
try {
MyType t1;
t = ( MyType ) o; // Fails. o is not MyType
if ( t1 != null )
{
// work with t1, it's a MyType.
} else
{
// Report a null reference failure.
}
} catch
{
// report the conversion failure.
}
// o is a SecondType:
MyType t = o as MyType; // Fails. o is not MyType
if ( t != null )
{
// work with t, it's a MyType.
} else
{
// report the failure.
}
// Version two:
try {
MyType t1;
t = ( MyType ) o; // Fails. o is not MyType
if ( t1 != null )
{
// work with t1, it's a MyType.
} else
{
// Report a null reference failure.
}
} catch
{
// report the conversion failure.
}
如果你使用下面的代码段,你应该可以成功的完成从SecondType到MyType的转换:
+展开
-C#
object o = Factory.GetObject( );
// Version three:
SecondType st = o as SecondType;
try {
MyType t;
t = ( MyType ) st;
if ( t != null )
{
// work with T, it's a MyType.
} else
{
// Report a null reference failure.
}
} catch
{
// report the failure.
}
// Version three:
SecondType st = o as SecondType;
try {
MyType t;
t = ( MyType ) st;
if ( t != null )
{
// work with T, it's a MyType.
} else
{
// Report a null reference failure.
}
} catch
{
// report the failure.
}
你决不应该写出如果糟糕的代码,但它确实解决了一个很常见的难题。尽管你决不应该这样写代码,但你可以写一个函数,用一个System.Object参数来完成正确的转换:
+展开
-C#
object o = Factory.GetObject( );
DoStuffWithObject( o );
private void DoStuffWithObject( object o2 )
{
try {
MyType t;
t = ( MyType ) o2; // Fails. o is not MyType
if ( t != null )
{
// work with T, it's a MyType.
} else
{
// Report a null reference failure.
}
} catch
{
// report the conversion failure.
}
}
DoStuffWithObject( o );
private void DoStuffWithObject( object o2 )
{
try {
MyType t;
t = ( MyType ) o2; // Fails. o is not MyType
if ( t != null )
{
// work with T, it's a MyType.
} else
{
// Report a null reference failure.
}
} catch
{
// report the conversion failure.
}
}
记住,对一个用户定义类型的对象,转换操作只是在编译时,而不是在运行时。在运行时存在介于o2和MyType之间的转换并没有关系,(因为)编译器并不知道也不关心这些。这样的语句有不同的行为,这要取决于对st类型的申明:
t = ( MyType ) st;
(译注:上面说的有些模糊。为什么上面的代码可能会有不同的行为的呢?不同的什么行为呢?主要就是:上面的这个转化,是在编译时还是在运行时!如果st是用户定义的类型,那么上面的转换是在编译时。编译器把st当成为System.Object类型来编译生成的IL代码,因此在运行时是无法把一个Object类型转化为MyType类型的。解决办法就是前面提到的方法,多加一条语句,先把Object类型转换为SecondType,然后再强制转化为MyType类型。但是如果st是内置类型,那么它的转换是在运行时的,这样的转化或许会成功,看后面的说明。因此,类似这样的代码:MyType m_mytype = (m_secondType as SecondType) as MyType;是不能通过编译的,提示错误是无法在编译时把SecondType转化为MyType,即使是重写了转换操作符。)
但下面的转换只会有一种行为,而不管st是什么类型。
所以你应该选择as来转换对象,而不是强制类型转换。实际上,如果这些类型与继承没有关系,但是用户自己定义的转换操作符是存在的,那么下面的语句转换将会得到一个编译错误:t = st as MyType;现在你应该明白要尽可能的使用as,下面我们来讨论不能使用as的时候。as运算符对值类型是无效的,下面的代码无法通过编译:
+展开
-C#
object o = Factory.GetValue( );
int i = o as int; // Does not compile.
int i = o as int; // Does not compile.
这是因为整形(ints)数据是值类型,并且它们永远不会为null。当o不是一个整形的时候,i应该取什么值呢?不管你选择什么值,它都将是一个无效的整数。因此,as不能使用(在值类型数据上)。你可以坚持用强制转化:
+展开
-C#
object o = Factory.GetValue( );
int i = 0;
try {
i = ( int ) o;
} catch
{
i = 0;
}
int i = 0;
try {
i = ( int ) o;
} catch
{
i = 0;
}
但是你并没有必要这样坚持用强制转换。你可以用is语句取消可能因转换引发的异常:
+展开
-C#
object o = Factory.GetValue( );
int i = 0;
if ( o is int )
i = ( int ) o;
int i = 0;
if ( o is int )
i = ( int ) o;
(译注:is和as一样,都是类型转换安全的,它们在任何时候都不会在转换时发生异常,因此可以先用is来安全的判断一下数据类型。与as不同的时,is只是做类型检测并返回逻辑值,不做转换。)
如果o是其它可转化为整形的类型(译注:但o并不是真正的整形),例如double,那么is运算操作会返回false。对于null,is总是返回false。
is只应该在你无法用as进行转换时使用。
另外,这是无意义的冗余:
+展开
-C#
// correct, but redundant:
object o = Factory.GetObject( );
MyType t = null;
if ( o is MyType )
t = o as MyType;
object o = Factory.GetObject( );
MyType t = null;
if ( o is MyType )
t = o as MyType;
如果你写下面的代码,那么跟上面一样,都是冗余的:
+展开
-C#
// correct, but redundant:
object o = Factory.GetObject( );
MyType t = null;
if ( ( o as MyType ) != null )
t = o as MyType;
object o = Factory.GetObject( );
MyType t = null;
if ( ( o as MyType ) != null )
t = o as MyType;
这都是低效且冗余的。如果你使用as来转换数据,那么用is来做检测是不必要的。只用检测返回类型是否为null就行了,这很简单。
现在,你已经知道is,as和强制转换之间的区别了。而在foreach的循环中,是使用的哪一种转换呢?
+展开
-C#
public void UseCollection( IEnumerable theCollection )
{
foreach ( MyType t in theCollection )
t.DoStuff( );
}
{
foreach ( MyType t in theCollection )
t.DoStuff( );
}
foreach循环是用强制转换来完成把一个对象转换成循环可用的类型。上面的循环代码与下面手写的代码(hand-coded)是等效的:
+展开
-C#
public void UseCollection( IEnumerable theCollection )
{
IEnumerator it = theCollection.GetEnumerator( );
while ( it.MoveNext( ) )
{
MyType t = ( MyType ) it.Current;
t.DoStuff( );
}
}
{
IEnumerator it = theCollection.GetEnumerator( );
while ( it.MoveNext( ) )
{
MyType t = ( MyType ) it.Current;
t.DoStuff( );
}
}
foreach须要用强制转换来同时支持对值类型和引用类型的转换。通过选择强制转化,foreach循环就可以采用一样的行为,而不用管(循环)目标对象是什么类型。不管怎样,因为foreach循环是用的强制转换,因些它可能会产生BadCastExceptions的异常。
因为IEnumberator.Current返回一个System.Obejct对象,该对象没有(重写)转换操作符,所以它们没有一个满足(我们在上面做的)测试。
(译注:这里是说,如果你用一个集合存放了SecondType,而你又想用MyType来对它进行foreach循环,那么转换是失败的,原因是在循环时,并不是用SecondType,而是用的System.Object,因此,在foreach循环里做的转换与前面说的:MyType t = ( MyType ) o;是一样的错误,这里的o是SecondType,但是是以System.Object存在。)
正如你已经知道的,一个存放了SecondType的集合是不能在前面的函数UseCollection中使用循环的,这会是失败的。用强制转换的foreach循环不会在转换时检测循环集合中的对象是否具有有效的运行时类型。它只检测由IEnumerator.Current返回来的System.Object是否可转换为循环中使用的对象类型,这个例子中是MyType类型。
最后,有些时候你想知道一个对象的精确类型,而不仅仅是满足当前可转换的目标类型。as运算符在为任何一个从目标类型上派生的对象进行转换时返回true。GetType()方法取得一个对象的运行时对象,它提供了比is和as更严格的(类型)测试。GetType()返回的类型可以与指定的类型进行比较(,从而知道它是不是我们想要的类型)。
再次考虑这个函数:
+展开
如果你添加了一个派生自MyType的新类NewType,那么一个存放了NewType类型对象的集合可以很好的在UseCollection 函数中工作。 -C#
public void UseCollection( IEnumerable theCollection )
{
foreach ( MyType t in theCollection )
t.DoStuff( );
}
{
foreach ( MyType t in theCollection )
t.DoStuff( );
}
+展开
-C#
public class NewType : MyType
{
// contents elided.
}
{
// contents elided.
}
如果你想要写一个函数,使它对所有MyType类型的实例都能工作,上面的方法是非常不错的。如果你只想写一个函数,只对精确的MyType对象有效,那你必须用精确的类型比较。这里,你可以在foreach循环的内部完成。大多数时候,认为运行时确定对象的精确类型是很重要的,是在为对象做相等测试的时候。在大多数其它的比较中,由is和as提供的.isinst(译注:IL指令)比较,从语义上讲已经是正确的了。
好的面向对象实践告诉我们,你应该避免类型转换,但有些时候我没别无选择。当你无法避免转换时,用(C#)语言为我们提供的is和as运算符来清楚的表达你的意思吧。不同方法的强制转换有不同的规则。从语义上讲,is和as在绝大多转换上正确的,并当目标对象是正确的类型时, 它们总是成功的。应该选择这些基本指令来转换对象,至少它们会如你所期望的那样成功或者失败;而不是选择强制类型转换,这转换会产生一些意想不到的副作用。
25.来一个新技术WCF
+展开
-C#
sing System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using SQLDMO;
using System.Threading;
[ServiceContract(Namespace = "Samples.Services")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class WCFServer
{
[OperationContract]
public void DataBack() //数据库备份
{
System.Timers.Timer t = new System.Timers.Timer(5000);//实例化Timer类,设置间隔时间为10000毫秒;
t.Elapsed += new System.Timers.ElapsedEventHandler(tt);//到达时间的时候执行事件;
t.AutoReset = true;//设置是执行一次(false)还是一直执行(true);
t.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件;
}
public void tt(object sender, EventArgs e)
{
SQLDMO.Backup oBackup = new SQLDMO.BackupClass();
SQLDMO.SQLServer oSQLServer = new SQLDMO.SQLServerClass();
try
{
oSQLServer.LoginSecure = false;
oSQLServer.Connect(".", "sa", "123");
oBackup.Action = SQLDMO.SQLDMO_BACKUP_TYPE.SQLDMOBackup_Database;
oBackup.Database = "MyBookShop";
oBackup.Files = @"c:\databak.bak";
oBackup.BackupSetName = "MyBookShopDB";
oBackup.BackupSetDescription = "数据库备份";
oBackup.Initialize = true;
oBackup.SQLBackup(oSQLServer);
}
catch
{
throw;
}
finally
{
oSQLServer.DisConnect();
}
}
[OperationContract]
public void DbRestore() //数据库恢复
{
System.Timers.Timer t = new System.Timers.Timer(10000);//实例化Timer类,设置间隔时间为10000毫秒;
t.Elapsed += new System.Timers.ElapsedEventHandler(ee);//到达时间的时候执行事件;
t.AutoReset = true;//设置是执行一次(false)还是一直执行(true);
t.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件;
}
public void ee(object sender, System.Timers.ElapsedEventArgs e)
{
string strDbName = "MyBookShopDB";
SQLDMO.Restore res = new SQLDMO.RestoreClass();
SQLDMO.SQLServer svr = new SQLDMO.SQLServerClass();
try
{
svr.LoginSecure = false;
svr.Connect(".", "sa", "123");
SQLDMO.QueryResults qr = svr.EnumProcesses(-1);
int iColPIDNum = -1;
int iColDbName = -1;
res.Database = strDbName;
for (int i = 1; i <= qr.Columns; i++)
{
string strName = qr.get_ColumnName(i);
if (strName.ToUpper().Trim() == "SPID")
{
iColPIDNum = i;
}
else if (strName.ToUpper().Trim() == "DBNAME")
{
iColDbName = i;
}
if (iColPIDNum != -1 && iColDbName != -1)
break;
}
for (int i = 1; i <= qr.Rows; i++)
{
int lPID = qr.GetColumnLong(i, iColPIDNum);
string strDBName = qr.GetColumnString(i, iColDbName);
if (strDBName.ToUpper() == strDbName.ToUpper())
svr.KillProcess(lPID);
}
res.Action = SQLDMO.SQLDMO_RESTORE_TYPE.SQLDMORestore_Database;
res.Database = strDbName ;
res.Files = @"c:\databak.bak";
res.FileNumber = 1;
res.ReplaceDatabase = true;
res.SQLRestore(svr);
}
catch (Exception err)
{
throw (new Exception("恢复数据库失败,请关闭所有和该数据库连接的程序!" + err.Message));
}
finally
{
svr.DisConnect();
}
}
}
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using SQLDMO;
using System.Threading;
[ServiceContract(Namespace = "Samples.Services")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class WCFServer
{
[OperationContract]
public void DataBack() //数据库备份
{
System.Timers.Timer t = new System.Timers.Timer(5000);//实例化Timer类,设置间隔时间为10000毫秒;
t.Elapsed += new System.Timers.ElapsedEventHandler(tt);//到达时间的时候执行事件;
t.AutoReset = true;//设置是执行一次(false)还是一直执行(true);
t.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件;
}
public void tt(object sender, EventArgs e)
{
SQLDMO.Backup oBackup = new SQLDMO.BackupClass();
SQLDMO.SQLServer oSQLServer = new SQLDMO.SQLServerClass();
try
{
oSQLServer.LoginSecure = false;
oSQLServer.Connect(".", "sa", "123");
oBackup.Action = SQLDMO.SQLDMO_BACKUP_TYPE.SQLDMOBackup_Database;
oBackup.Database = "MyBookShop";
oBackup.Files = @"c:\databak.bak";
oBackup.BackupSetName = "MyBookShopDB";
oBackup.BackupSetDescription = "数据库备份";
oBackup.Initialize = true;
oBackup.SQLBackup(oSQLServer);
}
catch
{
throw;
}
finally
{
oSQLServer.DisConnect();
}
}
[OperationContract]
public void DbRestore() //数据库恢复
{
System.Timers.Timer t = new System.Timers.Timer(10000);//实例化Timer类,设置间隔时间为10000毫秒;
t.Elapsed += new System.Timers.ElapsedEventHandler(ee);//到达时间的时候执行事件;
t.AutoReset = true;//设置是执行一次(false)还是一直执行(true);
t.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件;
}
public void ee(object sender, System.Timers.ElapsedEventArgs e)
{
string strDbName = "MyBookShopDB";
SQLDMO.Restore res = new SQLDMO.RestoreClass();
SQLDMO.SQLServer svr = new SQLDMO.SQLServerClass();
try
{
svr.LoginSecure = false;
svr.Connect(".", "sa", "123");
SQLDMO.QueryResults qr = svr.EnumProcesses(-1);
int iColPIDNum = -1;
int iColDbName = -1;
res.Database = strDbName;
for (int i = 1; i <= qr.Columns; i++)
{
string strName = qr.get_ColumnName(i);
if (strName.ToUpper().Trim() == "SPID")
{
iColPIDNum = i;
}
else if (strName.ToUpper().Trim() == "DBNAME")
{
iColDbName = i;
}
if (iColPIDNum != -1 && iColDbName != -1)
break;
}
for (int i = 1; i <= qr.Rows; i++)
{
int lPID = qr.GetColumnLong(i, iColPIDNum);
string strDBName = qr.GetColumnString(i, iColDbName);
if (strDBName.ToUpper() == strDbName.ToUpper())
svr.KillProcess(lPID);
}
res.Action = SQLDMO.SQLDMO_RESTORE_TYPE.SQLDMORestore_Database;
res.Database = strDbName ;
res.Files = @"c:\databak.bak";
res.FileNumber = 1;
res.ReplaceDatabase = true;
res.SQLRestore(svr);
}
catch (Exception err)
{
throw (new Exception("恢复数据库失败,请关闭所有和该数据库连接的程序!" + err.Message));
}
finally
{
svr.DisConnect();
}
}
}
26.呵呵,我就不贴了
实际上,微软和devxpress等公司都有公开的大堆的常用代码段
有兴趣可以去下devxpress的coderush(恩,好像现在叫DevExpress IDETools)
而微软的代码则在http://msdn.microsoft.com/zh-cn/vstudio/aa718338.aspx下载
使用代码段实际不只是知识点和小技巧,还有更现实的意义,代码段能够使你更方便快速的开发
27.刚刚写的通用删除数据方法
+展开
-C#
public Boolean DeleteEntity(Object obj)
{
Type type = obj.GetType();
String tableName = String.Empty;
Attribute att = Attribute.GetCustomAttribute(type, typeof(System.Data.Linq.Mapping.TableAttribute)) ;
if (att == null)
{
tableName = type.Name;
}
else
{
System.Data.Linq.Mapping.TableAttribute tableAttr = att as System.Data.Linq.Mapping.TableAttribute;
tableName = tableAttr.Name;
}
List<String> primaryKeyNames = new List<String>();
List<Object> values = new List<Object>();
PropertyInfo[] propertys = type.GetProperties();
if (propertys != null && propertys.Length > 0)
{
foreach(PropertyInfo p in propertys)
{
Attribute col = Attribute.GetCustomAttribute(p, typeof(System.Data.Linq.Mapping.ColumnAttribute));
if (col == null)
continue;
System.Data.Linq.Mapping.ColumnAttribute colAtt = col as System.Data.Linq.Mapping.ColumnAttribute;
if (colAtt.IsPrimaryKey)
{
primaryKeyNames.Add(p.Name);
values.Add(p.GetValue(obj, null));
}
}
List<String> condition = new List<string>();
for (Int32 i = 0; i < primaryKeyNames.Count;i++)
{
condition.Add(" " + primaryKeyNames[i] + " = {" + i + "}");
}
String sql = String.Format(DELETE,tableName,String.Join(" and ",condition.ToArray()));
Int32 count = baseDal.jyc.ExecuteCommand(sql, values.ToArray());
return count > 0 ? true : false;
}
return false;
}
{
Type type = obj.GetType();
String tableName = String.Empty;
Attribute att = Attribute.GetCustomAttribute(type, typeof(System.Data.Linq.Mapping.TableAttribute)) ;
if (att == null)
{
tableName = type.Name;
}
else
{
System.Data.Linq.Mapping.TableAttribute tableAttr = att as System.Data.Linq.Mapping.TableAttribute;
tableName = tableAttr.Name;
}
List<String> primaryKeyNames = new List<String>();
List<Object> values = new List<Object>();
PropertyInfo[] propertys = type.GetProperties();
if (propertys != null && propertys.Length > 0)
{
foreach(PropertyInfo p in propertys)
{
Attribute col = Attribute.GetCustomAttribute(p, typeof(System.Data.Linq.Mapping.ColumnAttribute));
if (col == null)
continue;
System.Data.Linq.Mapping.ColumnAttribute colAtt = col as System.Data.Linq.Mapping.ColumnAttribute;
if (colAtt.IsPrimaryKey)
{
primaryKeyNames.Add(p.Name);
values.Add(p.GetValue(obj, null));
}
}
List<String> condition = new List<string>();
for (Int32 i = 0; i < primaryKeyNames.Count;i++)
{
condition.Add(" " + primaryKeyNames[i] + " = {" + i + "}");
}
String sql = String.Format(DELETE,tableName,String.Join(" and ",condition.ToArray()));
Int32 count = baseDal.jyc.ExecuteCommand(sql, values.ToArray());
return count > 0 ? true : false;
}
return false;
}
28..net自带的List <T>.Sort(Icompare)就是这样的复杂度,关于Icompare可以利用虚函数(委托)实现。建议搜一下我说的词。
如果搜不到,另外开贴,我给大家分享一下。
29.你们逼我发biao的,C#技巧,当然要这么写啦:
1.如果可能尽量使用接口来编程
+展开
-C#
private void LoadList (object [] items, ListBox l)
{
for (int i = 0; i < items.Length;i++)
l.Items.Add (items[i].ToString ());
}
{
for (int i = 0; i < items.Length;i++)
l.Items.Add (items[i].ToString ());
}
注:这样可以提高代码稳定性!
2. 使用属性代替原始数据
+展开
-C#
private int TheMonth = 0;
[XmlAttribute ("Month")]
public int Month
{
get {
return TheMonth;
}
set {
TheMonth = value;
}
}
[XmlAttribute ("Month")]
public int Month
{
get {
return TheMonth;
}
set {
TheMonth = value;
}
}
注:代码会更灵活,利于封装!
3. 在Producer/Consumer 的Idiom中使用Delegate
+展开
-C#
public class KeyboardProcessor
{
private OnGetLine theFunc = null;
public OnGetLine OnGetLineCallback {
get {
return theFunc;
}
set {
theFunc = value;
}
}
public void Run (){
// Read input.
// If there is any listeners, publish:
string s;
do {
s = Console.ReadLine ();
if (s.Length == 0)
break;
if (theFunc != null){
System.Delegate [] funcs =theFunc.GetInvocationList();
foreach (OnGetLine f in funcs) {
try {
f (s);
} catch (Exception e) {
Console.WriteLine
("Caught Exception: {0}", e.Message);
}
}
}
} while (true);
}
{
private OnGetLine theFunc = null;
public OnGetLine OnGetLineCallback {
get {
return theFunc;
}
set {
theFunc = value;
}
}
public void Run (){
// Read input.
// If there is any listeners, publish:
string s;
do {
s = Console.ReadLine ();
if (s.Length == 0)
break;
if (theFunc != null){
System.Delegate [] funcs =theFunc.GetInvocationList();
foreach (OnGetLine f in funcs) {
try {
f (s);
} catch (Exception e) {
Console.WriteLine
("Caught Exception: {0}", e.Message);
}
}
}
} while (true);
}
注:这样做的好处是可以降低耦合!
4. 注意初始化顺序
这个我就不代码说明了,想想就清楚了呗,从上而下的设计原则,当然要注意.
30.累加:
+展开
-C#
Seq.fold ( + ) 0 [1..100000];;
从 1 加到十万……一行解决……
31.
1.使用resharper,尤其是使用其中的Refactory,来修改名称,做参数调整。。。等重构动作
2.把常用的代码段拉入左边的代码片段,下次使用的时候,直接将代码片段拖入即可。
32.不用临时变量交换两个int变量的值
+展开
-C#
int a=1;
int b=2;
a=b+(b=a)*0;
int b=2;
a=b+(b=a)*0;
33.try和catch块写在循环体的外面:
+展开
-C#
// 使用下面的写法
try {
for(int i=0; i<100; i++) {}
} catch(Exception) {
}
// 不要使用下面的写法
for(int i=0; i<100; i++) {
try{}catch{}
}
try {
for(int i=0; i<100; i++) {}
} catch(Exception) {
}
// 不要使用下面的写法
for(int i=0; i<100; i++) {
try{}catch{}
}
34.能提几个小问题吗?
1. 关于字段和属性,你大量的使用了字段,而非属性,当然,对程序不会有影响,但可能会影响到你的工资,哈哈。建议字段全部private,并公开一个属性去访问字段。
2. 关于try{}catch{}块,适当的使用try catch块能让程序更健壮,但看得出,你的这个类应该是一个公共类,是供其它类调用的,而你的catch却隐藏了所有的异常细节,换句话说,我传入一个完错误的sql,使用你的类去执行,只会得到一个false,却看不到任何有用的错误细节。非常不利于程序的调试。
3. 方法的命名规则,CloseRs()要关闭什么呢?SetTransaction()通常理解为对字段的赋值,却又没有参数,StartTransaction()会不会更好?(不要使用Begin开头的方法,除非这是个异步的方法)
4. 方法的返回值,没必要将每个方法的执行结果使用布尔值返回给用户,void就可以。而且,方法失败建议直接抛出异常,而不是告诉调用代码,“我挂了”,因为你抛出异常,人家就知道你“挂”了
5. 使用枚举提高代码的可用性,如DataType就可以设置成枚举类型。
就这些,纯属个人浅见,见笑。
35.空判断操作:
+展开
-C#
string str=string.Empty;
if (string.IsNullOrEmpty(str))
。。。
else
。。。。
end
if (string.IsNullOrEmpty(str))
。。。
else
。。。。
end
36.获取html源文件中所有a标记, 且分离出其中的href地址以及innerText 内容
+展开
-C#
string x =“获取的网站html代码”;
Regex r = new Regex(" <a.*?href=[\"\']{0,1}(? <h>.*?)(?= |\"|\'|>)(.*?>)(? <inner>.*?) </a>");
MatchCollection ms = r.Matches(x);
Console.WriteLine(ms.Count);
foreach (Match m in ms)
{
Console.WriteLine("all:" + m.Groups[0].Value);
Console.WriteLine("href:" + m.Groups["h"].Value);
Console.WriteLine("inner:" + m.Groups["inner"].Value);
Console.WriteLine();
}
Regex r = new Regex(" <a.*?href=[\"\']{0,1}(? <h>.*?)(?= |\"|\'|>)(.*?>)(? <inner>.*?) </a>");
MatchCollection ms = r.Matches(x);
Console.WriteLine(ms.Count);
foreach (Match m in ms)
{
Console.WriteLine("all:" + m.Groups[0].Value);
Console.WriteLine("href:" + m.Groups["h"].Value);
Console.WriteLine("inner:" + m.Groups["inner"].Value);
Console.WriteLine();
}
加支付宝好友偷能量挖...