编写高质量代码改善C#程序的157个建议
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
查看以下代码,比较下面两种字符串拼接方式,哪种效率更高:
private static void NewMethod1() { www.jnmu8.com
string a = "t"; a += "e"; a += "s"; a += "t"; }
private static void NewMethod2() {
string a = "t"; string b = "e";
string c = "s"; string d = "t"; string result = a + b + c + d; }
结果可以得知:两者的效率都不高。不要以为前者比后者创建的字符串对象更少,事实 上,两者创建的字符串对象相源自文库,且前者进行了 3 次 string.Contact 方法调用,比后者还 多了两次。
private static void NewMethod1() {
string s1="abc"; s1="123"+s1+"456"; ////以上两行代码创建了 3 个字符串对象对象,并 执行了一次 string.Contact 方法 }
private static void NewMethod2() {
{
SecondType secondType = new SecondType() { Name = "转型
用户自定义的类型也可以通过重载转换运算符的方式提供这一类转换:
public class Ip {
IPAddress value; public Ip(string ip) {
value = IPAddress.Parse(ip); }
//重载转换运算符,implicit 关键字用于声明隐式的用户定义类型转换 运算符。
public static implicit operator Ip(string ip) {
Ip iptemp = new Ip(ip); return iptemp; }
//重写 ToString 方法 public override string ToString() {
return value.ToString(); } } class Program { public static void Main(string[] args)
针对第一种情况:
public class FirstType {
public string Name { get; set; } }
public class SecondType {
public string Name { get; set; }
public static explicit operator SecondType(FirstType firstType)
{ Ip ip = "192.168.1.1"; //通过 Ip 类的重载转换运算符,实现字符
串到 Ip 类型的隐式转换 Console.WriteLine(ip.ToString()); Console.ReadLine();
} }
提供的就是字符串到类型 Ip 之间的隐式转换。
2、使用类型内置的 Parse、TryParse,或者如 ToString、ToDouble、ToDateTi me 等方法
string re2="123"+"abc"+"456"; 6"; }
///该代码等效于///string re2="123abc45
private static void NewMethod4() {
const string a="t"; string re="abc"+a; ///因为 a 是一个常量,所以该代码等效于 string=re=" abc"+"t"; 最终等效于 string re="abct"; }
首先需要明确强制转换可能意味这两件不同的事情:
1、FirstType 和 SecondType 彼此依靠转换操作来完成两个类型之间的转换。 2、FirstType 是 SecondType 的基类。 类型之间如果存在强制转换,那么它们之间的关系要么是第一种,要么是第二种。不可能同 时是继承的关系,又提供了转型符。
1、确保尽量少的装箱
2、避免分配额外的内存空间
先来介绍第一个方面,请看下面的两行代码:
String str1="str1"+9; String str2="str2"+9.ToString();
从 IL 代码可以得知,第一行代码在运行时完成一次装箱的行为,而第二行代码中并没有发 生装箱的行为,它实际调用的是整型的 ToString()方法,效率要比装箱高。所以,在使用 其他值引用类型到字符串的转换并完成拼接时,应当避免使用操作符“+”来完成,而应该使 用值引用类型提供的 ToString()方法。
要完成这样的运行时字符串拼接(注意:是运行时),更佳的做法是使用 StringBuil der 类型,代码如下所示:
public static void NewMethod() {
////定义了四个变量 string a = "t"; string b = "e"; string c = "s"; string d = "t"; StringBuilder sb = new StringBuilder(a); sb.Append(b); sb.Append(c); sb.Append(d); www.qqtop1.com ///提示是运行时,所以没有使用以下代码 //StringBuilder sb = new StringBuilder("t"); //sb.Append("e"); //sb.Append("s"); //sb.Append("t"); //string result = sb.ToString(); }
比如从 string 转换为 int,因为其经常发生,所以 int 本身就提供了 Parse 和 TryParse 方 法。一般情况下,如果要对某类型进行转换操作,建议先查阅该类型的 API 文档。
3、使用帮助类提供的方法
可以使用 System.Convert 类、System.BitConverter 类来进行类型的转换。
对于 String 和 StringBuilder 的简单介绍也可以参考我之前的一篇文章 http://www.cn blogs.com/aehyok/p/3505000.html
建议 2、使用默认转型方法
1、使用类型的转换运算符,其实就是使用类型内部的一方方法(即函数)。转换运算符分 为两类:隐式转换和显式转换(强制转换)。基元类型普遍都提供了转换运算符。
由于使用 System.String 类会在某些场合带来明显的性能损耗,所以微软另外提供了一个 类型 StringBuilder 来弥补 String 的不足。
StringBuilder 并不会重新创建一个 string 对象,它的效率源于预先以非托管的方式分配 内存。如果 StringBuilder 没有先定义长度,则默认分配的长度为 16。当 StringBuilder 字符串长度小于等于 16 时,StringBuilder 不会重新分配内存;当 StringBuilder 字符长 度大于 16 小于 32 时,StringBuilder 又会重新分配内存,使之成为 16 的倍数。在上面 的代码中,如果预先判断字符串的长度将大于 16,则可以为其设定一个更加合适的长度(如 32)。StringBuilder 重新分配内存时是按照上次容量加倍进行分配的。当然,我们需要 注意,StringBuilder 指定的长度要合适,太小了,需要频繁分配内存,太大了,浪费空间。
继承 IConvertible 接口必须同时实现其他转型方法,如上文的 ToBoolean、ToByte,如 果不支持此类转型,则应该抛出一个 InvalidCastException,而不是一个 NotImplement edException。 4、使用 CLR 支持的转型 CLR 支持的转型,即上溯转型和下溯转型。这个概念首先是在 Java 中提出来的,实际上就 是基类和子类之间的相互转换。
所谓“基元类型”,是指编译器直接支持的数据类型。基元类型包括:sbyte、byte、short、 ushort、int、uint、long、ulong、char、float、double、bool、decimal、object、s tring。
int i = 0; float j = 0; j = i; ///int 到 float 存在一个隐式转换 i = (int)j; ///float 到 int 必须存在一个显式转换
System.Convert 提供了将一个基元类型转换为其他基元类型的方法,如 ToChar、ToBo olean 方法等。值得注意的是,System.Convert 还支持将任何自定义类型转换为任何基 元类型,只要自定义类型继承了 IConvertible 接口就可以。如上文中的 IP 类,如果将 Ip 转换为 string,除了重写 Object 的 ToString 方法外,还可以实现 IConvertible 的 ToStr ing()方法
第二方面,避免分配额外的内存空间。对 CLR 来说,string 对象(字符串对象)是个很特 殊的对象,它一旦被赋值就不可改变。在运行时调用 System.String 类中的任何方法或进 行任何运算(如“=”赋值、“+”拼接等),都会在内存中创建一个新的字符串对象,这也意 味着要为该新对象分配新的内存空间。像下面的代码就会带来运行时的额外开销。
建议 3、区别对待强制转换与 as 和 is
首先来看一个简单的实例
FirstType firstType = new FirstType(); SecondType secondType = new SecondType(); secondType = (SecondType)firstType;
从上面的三行代码可以看出,类似上面的应该就是强制转换。
string re=9+"456"; ////该方法发生了一次装箱,并调用一次 string.Cont act 方法
}
关于装箱拆箱的问题大家可以查看我之前的文章 而以下代码,字符串不会在运行时进行拼接,而是会在编译时直接生成一个字符串。
private static void NewMethod3() {
就比如: 动作 Animal 类、Dog 类继承 Animal 类、Cat 类也继承自 Amimal 类。在进行 子类向基类转型的时候支持隐式转换,如 Dog 显然就是一个 Animal;而当 Animal 转型 为 Dog 的时候,必须是显式转换,因为 Animal 还可能是一个 Cat。
Animal animal = new Animal(); Dog dog = new Dog(); animal = dog; /////隐式转换,因为 Dog 就是 Animal ///dog=animal; ////编译不通过 dog = (dog)animal; /////必须存在一个显式转换
前言
本文主要来学习记录前三个建议。
建议 1、正确操作字符串
建议 2、使用默认转型方法
建议 3、区别对待强制转换与 as 和 is
其中有很多需要理解的东西,有些地方可能理解的不太到位,还望指正。
建议 1、正确操作字符串
字符串应该是所有编程语言中使用最频繁的一种基础数据类型。如果使用不慎,我们就 会为一次字符串的操作所带来的额外性能开销而付出代价。本条建议将从两个方面来探讨如 何规避这类性能开销:
微软还提供了另外一个方法来简化这种操作,即使用 string.Format 方法。string.Forma t 方法在内部使用 StringBuilder 进行字符串的格式化,代码如下所示:
public static void NewMethod4() {
string a = "t"; string b = "e"; string c = "s"; string d = "t"; string result = string.Format("{0}{1}{2}{3}", a, b, c, d); }
private static void NewMethod1() { www.jnmu8.com
string a = "t"; a += "e"; a += "s"; a += "t"; }
private static void NewMethod2() {
string a = "t"; string b = "e";
string c = "s"; string d = "t"; string result = a + b + c + d; }
结果可以得知:两者的效率都不高。不要以为前者比后者创建的字符串对象更少,事实 上,两者创建的字符串对象相源自文库,且前者进行了 3 次 string.Contact 方法调用,比后者还 多了两次。
private static void NewMethod1() {
string s1="abc"; s1="123"+s1+"456"; ////以上两行代码创建了 3 个字符串对象对象,并 执行了一次 string.Contact 方法 }
private static void NewMethod2() {
{
SecondType secondType = new SecondType() { Name = "转型
用户自定义的类型也可以通过重载转换运算符的方式提供这一类转换:
public class Ip {
IPAddress value; public Ip(string ip) {
value = IPAddress.Parse(ip); }
//重载转换运算符,implicit 关键字用于声明隐式的用户定义类型转换 运算符。
public static implicit operator Ip(string ip) {
Ip iptemp = new Ip(ip); return iptemp; }
//重写 ToString 方法 public override string ToString() {
return value.ToString(); } } class Program { public static void Main(string[] args)
针对第一种情况:
public class FirstType {
public string Name { get; set; } }
public class SecondType {
public string Name { get; set; }
public static explicit operator SecondType(FirstType firstType)
{ Ip ip = "192.168.1.1"; //通过 Ip 类的重载转换运算符,实现字符
串到 Ip 类型的隐式转换 Console.WriteLine(ip.ToString()); Console.ReadLine();
} }
提供的就是字符串到类型 Ip 之间的隐式转换。
2、使用类型内置的 Parse、TryParse,或者如 ToString、ToDouble、ToDateTi me 等方法
string re2="123"+"abc"+"456"; 6"; }
///该代码等效于///string re2="123abc45
private static void NewMethod4() {
const string a="t"; string re="abc"+a; ///因为 a 是一个常量,所以该代码等效于 string=re=" abc"+"t"; 最终等效于 string re="abct"; }
首先需要明确强制转换可能意味这两件不同的事情:
1、FirstType 和 SecondType 彼此依靠转换操作来完成两个类型之间的转换。 2、FirstType 是 SecondType 的基类。 类型之间如果存在强制转换,那么它们之间的关系要么是第一种,要么是第二种。不可能同 时是继承的关系,又提供了转型符。
1、确保尽量少的装箱
2、避免分配额外的内存空间
先来介绍第一个方面,请看下面的两行代码:
String str1="str1"+9; String str2="str2"+9.ToString();
从 IL 代码可以得知,第一行代码在运行时完成一次装箱的行为,而第二行代码中并没有发 生装箱的行为,它实际调用的是整型的 ToString()方法,效率要比装箱高。所以,在使用 其他值引用类型到字符串的转换并完成拼接时,应当避免使用操作符“+”来完成,而应该使 用值引用类型提供的 ToString()方法。
要完成这样的运行时字符串拼接(注意:是运行时),更佳的做法是使用 StringBuil der 类型,代码如下所示:
public static void NewMethod() {
////定义了四个变量 string a = "t"; string b = "e"; string c = "s"; string d = "t"; StringBuilder sb = new StringBuilder(a); sb.Append(b); sb.Append(c); sb.Append(d); www.qqtop1.com ///提示是运行时,所以没有使用以下代码 //StringBuilder sb = new StringBuilder("t"); //sb.Append("e"); //sb.Append("s"); //sb.Append("t"); //string result = sb.ToString(); }
比如从 string 转换为 int,因为其经常发生,所以 int 本身就提供了 Parse 和 TryParse 方 法。一般情况下,如果要对某类型进行转换操作,建议先查阅该类型的 API 文档。
3、使用帮助类提供的方法
可以使用 System.Convert 类、System.BitConverter 类来进行类型的转换。
对于 String 和 StringBuilder 的简单介绍也可以参考我之前的一篇文章 http://www.cn blogs.com/aehyok/p/3505000.html
建议 2、使用默认转型方法
1、使用类型的转换运算符,其实就是使用类型内部的一方方法(即函数)。转换运算符分 为两类:隐式转换和显式转换(强制转换)。基元类型普遍都提供了转换运算符。
由于使用 System.String 类会在某些场合带来明显的性能损耗,所以微软另外提供了一个 类型 StringBuilder 来弥补 String 的不足。
StringBuilder 并不会重新创建一个 string 对象,它的效率源于预先以非托管的方式分配 内存。如果 StringBuilder 没有先定义长度,则默认分配的长度为 16。当 StringBuilder 字符串长度小于等于 16 时,StringBuilder 不会重新分配内存;当 StringBuilder 字符长 度大于 16 小于 32 时,StringBuilder 又会重新分配内存,使之成为 16 的倍数。在上面 的代码中,如果预先判断字符串的长度将大于 16,则可以为其设定一个更加合适的长度(如 32)。StringBuilder 重新分配内存时是按照上次容量加倍进行分配的。当然,我们需要 注意,StringBuilder 指定的长度要合适,太小了,需要频繁分配内存,太大了,浪费空间。
继承 IConvertible 接口必须同时实现其他转型方法,如上文的 ToBoolean、ToByte,如 果不支持此类转型,则应该抛出一个 InvalidCastException,而不是一个 NotImplement edException。 4、使用 CLR 支持的转型 CLR 支持的转型,即上溯转型和下溯转型。这个概念首先是在 Java 中提出来的,实际上就 是基类和子类之间的相互转换。
所谓“基元类型”,是指编译器直接支持的数据类型。基元类型包括:sbyte、byte、short、 ushort、int、uint、long、ulong、char、float、double、bool、decimal、object、s tring。
int i = 0; float j = 0; j = i; ///int 到 float 存在一个隐式转换 i = (int)j; ///float 到 int 必须存在一个显式转换
System.Convert 提供了将一个基元类型转换为其他基元类型的方法,如 ToChar、ToBo olean 方法等。值得注意的是,System.Convert 还支持将任何自定义类型转换为任何基 元类型,只要自定义类型继承了 IConvertible 接口就可以。如上文中的 IP 类,如果将 Ip 转换为 string,除了重写 Object 的 ToString 方法外,还可以实现 IConvertible 的 ToStr ing()方法
第二方面,避免分配额外的内存空间。对 CLR 来说,string 对象(字符串对象)是个很特 殊的对象,它一旦被赋值就不可改变。在运行时调用 System.String 类中的任何方法或进 行任何运算(如“=”赋值、“+”拼接等),都会在内存中创建一个新的字符串对象,这也意 味着要为该新对象分配新的内存空间。像下面的代码就会带来运行时的额外开销。
建议 3、区别对待强制转换与 as 和 is
首先来看一个简单的实例
FirstType firstType = new FirstType(); SecondType secondType = new SecondType(); secondType = (SecondType)firstType;
从上面的三行代码可以看出,类似上面的应该就是强制转换。
string re=9+"456"; ////该方法发生了一次装箱,并调用一次 string.Cont act 方法
}
关于装箱拆箱的问题大家可以查看我之前的文章 而以下代码,字符串不会在运行时进行拼接,而是会在编译时直接生成一个字符串。
private static void NewMethod3() {
就比如: 动作 Animal 类、Dog 类继承 Animal 类、Cat 类也继承自 Amimal 类。在进行 子类向基类转型的时候支持隐式转换,如 Dog 显然就是一个 Animal;而当 Animal 转型 为 Dog 的时候,必须是显式转换,因为 Animal 还可能是一个 Cat。
Animal animal = new Animal(); Dog dog = new Dog(); animal = dog; /////隐式转换,因为 Dog 就是 Animal ///dog=animal; ////编译不通过 dog = (dog)animal; /////必须存在一个显式转换
前言
本文主要来学习记录前三个建议。
建议 1、正确操作字符串
建议 2、使用默认转型方法
建议 3、区别对待强制转换与 as 和 is
其中有很多需要理解的东西,有些地方可能理解的不太到位,还望指正。
建议 1、正确操作字符串
字符串应该是所有编程语言中使用最频繁的一种基础数据类型。如果使用不慎,我们就 会为一次字符串的操作所带来的额外性能开销而付出代价。本条建议将从两个方面来探讨如 何规避这类性能开销:
微软还提供了另外一个方法来简化这种操作,即使用 string.Format 方法。string.Forma t 方法在内部使用 StringBuilder 进行字符串的格式化,代码如下所示:
public static void NewMethod4() {
string a = "t"; string b = "e"; string c = "s"; string d = "t"; string result = string.Format("{0}{1}{2}{3}", a, b, c, d); }