1.设置全局溢出检查,项目属性->生成->高级->检测运算上溢/下溢
2.局部使用溢出检测
Byte b = 100; b = checked((Byte)(b + 200));// 不检测溢出 checked// 检测溢出代码段 { Byte b = 100; b = (Byte)(b + 200); } —————————————————————————— UInt32 invalid = unchecked((UInt32)(-1));// 检测溢出 unchecked// 不检测溢出代码段 { Byte b = 100; b = (Byte)(b + 200); }
CLR支持2种类型:引用类型和值类型
引用类型总是从托管堆分配,C#的new操作符返回对象内存地址——即指向对象的内存地址。使用引用类型必须留意性能问题。注意4个事实
1.内存必须从托管堆分配(new的时候进行内存分配)
2.堆上分配的每个对象都有一些额外成员,这些成员必须初始化。
3.对象中的其他字节(为字段而设)总是设为0
4.从托管堆分配对象时,可能强制执行一次垃圾回收
引用类型和值类型的区别:
任何称为“类”的类型都是引用类型。(如:System.Exception类,System.IO.FileStream类以及System.Radom类)
所有值类型都称为结构或者枚举,(如:System.Int32结构,SystemBoolean结构,SystemDecimal结构,System.TimeSpan结构,System.DayOfWeek枚举,System.IO.FileAttributes枚举以及System.Drawing.FontStyle枚举)所有结构都是抽象类型System.ValueType的直接派生类。System.ValueType本身又直接从System.Object派生。根据定义,所有值类型都必须从System.ValueType派生。所有枚举都是从System.Enum抽象类型派生,后者又从System.ValueType派生。
class SomeRef { public Int32 x;}// 所有的class都是引用类型 struct SomeVal { public Int32 x;}// 所有的Struct都是值类型 static void ValueTypeDemo() { SomeRef r1 = new SomeRef();// 在堆上分配 SomeVal v1 = new SomeVal();// 在栈上分配 r1.x = 5; // 提领指针 v1.x = 5; // 在栈上修改 Console.WriteLine(r1.x); // 显示5 Console.WriteLine(v1.x); // 显示5 // SomeRef r2 = r1; // 只复制引用(指针/地址) SomeVal v2 = v1; // 在栈上分配并复制成员 r1.x = 8; // r1.x和r2.x都会更改 v1.x = 9; // v1.x会更改,v2.x不变 Console.WriteLine(r1.x); // 显示8 Console.WriteLine(r2.x); // 显示8 Console.WriteLine(v1.x); // 显示9 Console.WriteLine(v2.x); // 显示5 SomeVal v1=new SomeVal(); //在栈上分配,所有的字段初始化为0(已初始化) =>SomeVal v1; //在栈上分配,同样初始化为0(会认为v1未赋值) }
将值类型变量赋给另一个值类型变量,会逐行逐字段的复制,将引用类型的变量赋给另一个引用类型的变量只复制内存地址。
装箱是将值类型转换成引用类型,拆箱是将引用类型转换成值类型
public static void Main() { Point p; p.x = p.y =1; Object o = p; // 对p装箱:o引用已装箱实例 p = (Point) o; // 对o拆箱,将字段从已装箱实例复制到栈变量中,C#编译器将生成一条IL指令对o拆箱(获取毅装箱实例中的字段的地址),并生成另一条指令将这些字段从堆复制到基于栈的变量p中 p.x = 2; // 更新栈变量状态 o = p; // 对p装箱:o引用新的已装箱实例 }
要获取对值类型实例的引用,实例就必须装箱,将值类型实例传给需要获取引用类型的方法,就会发生这种情况
public sealed class Program { public static void Main() { Int32 v = 5; // 创建未装箱的值类型变量 #if INEFFICIENT // 编译下面这一行,v被装箱3次,浪费时间和内存 Console.WriteLine("{0},{1},{2}", v, v, v); #else // 下面代码结果一样,但无论执行速度,还是内存利用,都比前面代码更胜一筹 Object o = v; // 对v进行手动装箱(1次) // 编译下面这行不发生装箱 Console.WriteLine("{0},{1},{2}", o, o, o); } }