内容摘要
1 数据类型
1.1表达范围问题
1.2 数组的长度问题
1.3 值类型与引用类型
1.4 匿名类型与隐式类型
1.5硬编码造成的精度丢失问题
2 控制流语句
2.1 foreach语句
2.2 if-else语句
2.3 for循环
2.4 Switch语句
3 类
3.1 配置文件中使用静态字段或静态属性 6
3.2 虚方法中不要含有业务逻辑
3.3 指定构造器
3.4 对比virtual与abstract
3.5默认初始值
3.6 readonly
3.7 构造函数的调用问题
3.8 静态类和静态成员
3.9 可访问性
3.10尽量使用属性而不是字段
3.11 接口中的属性
4 泛型
4.1string.Join方法不能识别泛型
5 集合
5.1 关于集合的标准查询运算符
5.2 提高集合插入性能
6 异常处理
6.1 优先考虑在最外层捕获异常
6.2 try...finally与return
7 扩展
7.1对System.Linq的行为进行扩展
1 数据类型
1.1表达范围问题
int类型只能表达-232 至232 -1范围内的数据,float和double比int范围要宽的多,所以在使用这些类型时注意挑选合适的类型使用,另外与金融计算相关时使用decimal。
1.2 数组的长度问题
定义一个数组时最大的长度是多少?理论上最大长度为int.MaxValue。对于32位有符号整数来说最大值为2147483647,64位有符号整数最大值为9223372036854775807。一般来讲不会定义太长的数组,因为这样会比较消耗内存。
1.3 值类型与引用类型
string为引用类型,但下面的方法不会改变其自身:
string str =”abc”;
str.ToUpper();
上面的代码不会将其转为大写,下面写法可行:
str = str.ToUpper();
1.4 匿名类型与隐式类型
C#的匿名类型没有名称,是由编译器动态生成的数据类型,但它仍然是强类型。对匿名类型来说,不可能指定数据类型,所以声明匿名类型变量要使用var。
使用var来声明隐式类型。但对于数据类型并非匿名类型的情况下,建议使用显示数据类型。
var anonymous1 = new { Field1 = "sss", Field2 = "bbb" };
var anonymous2 = new { Field1 = "sss", Field2 = "CCC" };
var anonymous3 = new { Field1 = "ttt" };
var iy = "string";
无法将anonymous1与anonymous3互相赋值,无法将nim 与iy互相赋值,但anonymous1与anonymous2可以互相赋值
1.5硬编码造成的精度丢失问题
如果输入的数字字面值是含有小数,那么计算时默认为double类型,不含有小数,则认为是int类型;以f,d,m结尾的数被认为是float,double,decimal。所以涉及的相关运算时,注意写法。例如:
//运算结果为1.0
float res = 3 / 2;
//运算结果为1.5
float ress = 3f / 2f;
//这是错误写法,因为2.2这种写法是一个double类型
float f = 2.2;
//最后一个字符为f或F则表示float类型
float ff = 2.2f;
//这是错误写法,因为2.2是double类型,所以运算结果为double类型,无法将double类型隐式转换为float类型
//强制转换可以,但是可能会造成精度丢失。
float fff = 1 / 2.2;
2 控制流语句
2.1 foreach语句
使用foreach语句操作集合,禁止循环操作过程中修改集合中的元素。
try { List<string> list = new List<string> { "first", "second", "administrator", "letter", "join" }; foreach (var item in list) { if (item.Contains("l")) list.Remove(item); } } catch (Exception ex) { Console.WriteLine(ex.Message); }
异常信息:集合已修改;可能无法执行枚举操作。
异常类型:System.InvalidOperationException
2.2 if-else语句
if(condition1){}
else if(condition2){}
else if(condition3){}
else{}
与之等价的写法更好理解。
if(condition1)
else
{
if(condtion2)
else
{
if(condition3){}
else{}
}
}
2.3 for循环
一般循环的循环变量为int型,但是其他类型如float,double等也是可以使用的。
2.4 Switch语句
常常将Switch用作单一匹配,但不要忘记其多匹配功能,如下面的代码:
string sign ="b"; switch (sign) { case "a": case "b": Console.WriteLine("多匹配方式"); break; case "z": break; default: break; }
还可以向下面这样,使用Switch和return组合。
private int Switch() { int i = 0; switch (i) { case 0: { return 0; } case 1: { return 1; } default: { return 2; } } }
3 类
3.1 配置文件中使用静态字段或静态属性
静态变量是在静态变量所属类初次使用时被初始化的,当静态字段被初始化后,之后每次调用获得的值都是初始化时赋给静态字段的值,除非在这个过程中显示地给静态字段赋值。而静态属性的某些行为类似于静态方法。如下例:
public class Sys { /// <summary> /// 执行时间 /// </summary> public static string ExecuteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); public static string ExecuteDate { get { return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); } set { } } } static void Main(string[] args) { Console.WriteLine("字段:"+Sys.ExecuteTime); Console.WriteLine("属性:"+Sys.ExecuteDate); Thread.Sleep(2000); Console.WriteLine("字段0:" + Sys.ExecuteTime); Console.WriteLine("属性0:" + Sys.ExecuteDate); Thread.Sleep(2000); Sys.ExecuteDate = DateTime.Now.ToString("yyyy"); Console.WriteLine("字段1:" + Sys.ExecuteTime); Console.WriteLine("属性1:" + Sys.ExecuteDate); Console.Read(); }
运行上述代码,输出结果如下:
由输出结果可以得出:上面的用法中,静态字段每次调用获得的值都是同一个,即初始化时所赋的值;而调用静态属性每次获得的值都是不同的,每次调用都执行一次get方法。
3.2 虚方法中不要含有业务逻辑
使用virtual修饰符修饰类的方法,那么这个方法就可以在派生类中重写,如果原来的方法包含业务逻辑,派生类重写这个方法后,由于派生类将父类中的虚方法完全覆盖,导致虚方法中的业务逻辑永远不会被执行。
3.3 指定构造器
为了避免因缺少可供访问的默认构造器而造成错误,要在派生类构造器的头部显示指定要运行哪一个基类构造器。
3.4 对比virtual与abstract
为支持重写,基类中必须为要在子类中重写的成员之前添加virtual修饰符,子类成员要标记为override。
使用abstract定义抽象方法。抽象方法没有具体实现,必须在子类方法中实现抽象方法。
虚方法是可以有具体实现的,不过具体实现会在子类的重载中被覆盖。
3.5默认初始值
字段或属性默认初始值随类型的不同而不同。
bool默认初始值为false,对象类型默认初始值为null,int类型为0,float和double为0.0,char为