改善C#程序的157个建议(2)
4. TryParse比Parse好
1)TryParse,Parse比较两者的异同
相同点:两者都可以将字符串转型为本身的类型。
不同点:如果字符串不满足转换要求。parse方法会引发一个异常,TryParse方法则不会引发异常,它会返回false,同时将result置为0
引发异常这个过程会对性能造成损耗,所以TryParse比Parse好。
2)例如:
double re;
long ticks;
//double.Parse 运行成功的情况
Stopwatch sw = Stopwatch.StartNew();
for(int i = 0; i < 1000; i++) {
try {
re = double.Parse("123");
} catch {
re = 0;
}
}
sw.Stop();
ticks = sw.ElapsedTicks;
Console.WriteLine("double.Parse()成功,{0}ticks", ticks);
//double.TryParse 运行成功的情况
sw = Stopwatch.StartNew();
for (int i = 0; i < 1000; i++) {
if (double.TryParse("123", out re)==false) {
re = 0;
}
}
sw.Stop();
ticks = sw.ElapsedTicks;
Console.WriteLine("double.TryParse()成功,{0}ticks", ticks);
//double.Parse 运行失败的情况
sw = Stopwatch.StartNew();
for (int i = 0; i < 1000; i++) {
try {
re = double.Parse("abc");
} catch {
re = 0;
}
}
sw.Stop();
ticks = sw.ElapsedTicks;
Console.WriteLine("double.Parse()失败,{0}ticks", ticks);
//double.TryParse 运行失败的情况
sw = Stopwatch.StartNew();
for (int i = 0; i < 1000; i++) {
if (double.TryParse("abc", out re) == false) {
re = 0;
}
}
sw.Stop();
ticks = sw.ElapsedTicks;
Console.WriteLine("double.TryParse()失败,{0}ticks", ticks);
Console.ReadKey();
运行结果:
由此可见:
Tryparse和Parse如果执行成功,他们的效率在一个数量级上,甚至在本例循环内,TryParse所带来的效率比Parse还高一些。
如果执行失败,Parse的执行效率远远低于TryPase。
5. 使用int?来确保值类型也可以为null
1)基元类型为什么需要为null?考虑以下两个场景:
- 数据库中一个int字段可以被置为null,在C#中,值被取出来后,为了将他赋值给int类型,不得不手下判断一下他是否为null。如果将null值直接赋值给int类型会引发一场。
- 在一个分布式系统中,服务器需要接受并解析来自客户端的数据。一个int型数据可能在传输过程中丢失或被篡改了,转型失败后应该保存为null,而不是提供一个初始值。
2)Nullable是什么?
它是一个结构体,表示可为空的类型。
它可以简写为int?
3)可空类型与对应基元类型的相互转换
//基元类型提供了其对应可空类型的隐式转换
int? i = null;
int j = 0;
i = j;
Console.WriteLine("i={0},j={1}", i, j);
//可控类型不可隐式转换为对应的基元类型
int? ii = 123;
int jj;
//jj = ii;//不可以将一个可空类型隐式转换为对应的基元类型。
//正确的转换形式:
if(ii.HasValue) {//HasValue是用来判断,此参数是否有除了null以外的值;只有【数据类型?】才有HasValue属性
jj = ii.Value;
} else {
jj = 0;
}
Console.WriteLine("i={0},j={1}", i, j);
Console.ReadKey();
6. 区别readonly和const的用法
1)readonly与const的区别?
- const是一个编译器常量,readonly是一个运行时常量
- const只能修饰基元类型(对应引用类型的常熟,可能的值只能说string,null),而readonly没有限制。
- const默认就是静态的,而readonly如果设置为静态则需要显示声明。
- const字段只能在该字段的声明中使用常量初始化。readonly字段可以在声明或构造函数中初始化。在构造函数内,可以多次对readonly字赋值。
7. 将0值作为枚举的默认值
允许使用的枚举类型有byte,sbyte,short,ushort,int,uint,long,ulong。应该始终将0值作为枚举类型的默认值。
例如:
一个代表星期的枚举类Week
enum Week {
Monday=1,
Tuesday=2,
Wednesday=3,
Thursday=4,
Friday=5,
Saturday=6,
Sunday=7
}
static Week week;
static void Main(string[] args) {
Console.WriteLine(week);
Console.ReadKey();
}
输出结果:0
Week看上去多了第8个值,而且这段代码没有异常。所以,应该始终为枚举的0值指定默认值。比如在上面的枚举类型Week中,可以将显示为元素赋值去掉,编译器会自动从0值开始技术,然后逐个为元素的值+1
8. 避免给枚举类型的元素提供显示的值。
一般情况下,没必要给枚举类型的元素提供显示的值。创建枚举类型的理由之一就是为了代替使用实际的数值。不正确的为枚举类型的元素设定显式的值,会带来意想不到的错误。
例如:
enum Week {
Monday=1,
Tuesday=2,
ValueTemp,
Wednesday=3,
Thursday=4,
Friday=5,
Saturday=6,
Sunday=7
}
static void Main(string[] args) {
Week week = Week.ValueTemp;
Console.WriteLine(week);//Wednesday
Console.WriteLine(week==Week.Wednesday);//true
Console.ReadKey();
}
很显然这不是我们想要的结果。
为了避免不必要的错误,尽量避免给枚举类型的元素提供显示的值。
9. 习惯重载运算符。
在构建自己的类型时,我们应该始终考虑是否可以用于运算符重载。
例如:
class Program
{
static void Main(string[] args) {
Salary s1 = new Salary() { RMB = 22 };
Salary s2 = new Salary() { RMB = 33 };
Salary sum = s1 + s2;
Console.WriteLine(sum.RMB);//55
Console.ReadKey();
}
}
class Salary
{
public int RMB { get; set; }
public static Salary operator + (Salary s1,Salary s2) {
s2.RMB += s1.RMB;
return s2;
}
}