注意:被装过箱的对象才能被拆箱
拆箱必须非常小心,确保该值变量有足够的空间存储拆箱后得到的值。C#int只有32位,如果把64位的long值
拆箱为int时,会产生一个InvalidCastExecption异常。
显然,从原理上可以看出,装箱时,生成的是全新的引用对象,这会有时间损耗,也就是造成效率降低。装箱操作
和拆箱操作是要额外耗费cpu和内存资源的,所以在c# 2.0之后引入了泛型[泛型不存在boxing、unboxing,类型是安全的]来减少装箱操作和拆箱操作消耗。
值类型和引用类型
值类型:整型、浮点型、布尔型、枚举型、结构体等
引用类型:类、string、接口、数组、委托等
所有的值类型都是密封(seal)的。所以无法派生出新的值类型
值得注意的是,引用类型和值类型都是继承自System.Object类。不同的是,几乎所有的引用的类型都直接从Sytem.Object继承,
而值类型则继承其子类,即直接继承System.ValueType
System.ValueType直接派生于System.Object,即Sytem.ValueType本身是一个类类型,而不是值类型。其关键在于ValueType重写了Equals()方法,从而对值类型按照实例的值来比较,而不是引用地址来比较。
可以用Type.IsValueType属性来判断一个类型是否为值类型。
AAAA aa=new AAAA()
aa.GetType().IsValueType
string str = “str”;
int int_s = 50;
MessageBox.Show(str.GetType().IsValueType.ToString());
MessageBox.Show(int_s.GetType().IsValueType.ToString());



当我们要比较两个值类型【ValueType】变量是否相等式,可以调用继承自ValueType类型的Equals()方法。
这个复写的方法内部使用了反射,获得值类型所有字段,然后进行比较。
C#值类型,我们可以把他归纳成三类:
第一类: 基础数据类型(string类型除外):包括整型、浮点型、十进制型、布尔型。
整型包括:sbyte、byte、char、short、ushort、int、uint、long、ulong 这九种类型;
浮点型就包括 float 和 double 两种类型;
十进制型就是 decimal ;
布尔型就是 bool 型了。
第二类:结构类型:就是 struct 型
第三类:枚举类型:就是 enum 型
C#引用类型有五种:class、interface、delegate、object、string、Array。
所有值类型的数据都无法为null的(除可空类型修饰 int ?xx=null),声明后必须赋以初值;引用类型才允许为null。
可空类型
可空类型可以表示基础类型的所有值,另外还可以表示 null 值。
可空类型可通过下面两种方式中的一种声明:
System.Nullable variable
T? variable
T 是可空类型的基础类型。T 可以是包括 struct 在内的任何值类型;但不能是引用类型。
1.值类型后加问号表示此类型为可空类型,如int? i = null;
int? d = null;
System.Nullable e = null;
2.可空类型与一元或二元运算符一起使用时,只要有一个操作数为null,结果都为null;

3.比较可空类型时,只要一个操作数为null,比较结果就为false。

对于C#中的参数传递,根据参数的类型可以分为四类:
值类型参数的按值传递
引用类型参数的按值传递
值类型参数的按引用传递
引用类型参数的按引用传递
引用类型参数的按值传递
当传递的参数是引用类型的时候,传递和操作的是指向对象的引用(看到这里,有些朋友会觉得此时不是传递引用吗?怎
么还是按值传递了?对于这个疑惑,此时确实是按值传递,此时传递的对象的地址,传递地址本身也是传递这个地址的
值,所以此时仍然是按值传递的),此时方法的操作就会改变原来的对象。
class Program
{
static void Main(string[] args)
{
// 2. 引用类型按值传递情况
RefClass refClass = new RefClass();
AddRef(refClass);
Console.WriteLine(refClass.addnum);
}
// 2. 引用类型按值传递情况
private static void AddRef(RefClass addnumRef)
{
addnumRef.addnum += 1;
Console.WriteLine(addnumRef.addnum);
}
}
class RefClass
{
public int addnum=1;
}
// 上面结果都是2
String引用类型的按值传递的特殊情况
class Program
{
static void Main(string[] args)
{
// 3. String引用类型的按值传递的特殊情况
string str = “old string”;
ChangeStr(str);
Console.WriteLine(str);
}
// 3. String引用类型的按值传递的特殊情况
private static void ChangeStr(string oldStr)
{
oldStr = “New string”;
Console.WriteLine(oldStr);
}
}

不管是值类型还是引用类型,我们都可以使用ref 或out关键字来实现参数的按引用传递,然而按引用进行传递的时候,需
要注意下面两点:
方法的定义和方法调用都必须同时显式使用ref或out,否则会出现编译错误
CLR 允许out或ref参数来实现方法重载
#region CLR 允许out或ref参数来实现方法重载
private static void Add(string str)
{
Console.WriteLine(str);
}
// 编译器会认为下面的方法是另一个方法,从而实现方法重载
private static void Add(ref string str)
{
Console.WriteLine(str);
}
#endregion
class Program
{
static void Main(string[] args)
{
#region 按引用传递
Console.WriteLine(“按引用传递的情况”);
int num = 1;
string refStr = “Old string”;
ChangeByValue(ref num);
Console.WriteLine(num);
changeByRef(ref refStr);
Console.WriteLine(refStr);
#endregion
Console.Read();
}
#region 按引用传递
// 1. 值类型的按引用传递情况
private static void ChangeByValue(ref int numValue)
{
numValue = 10;
Console.WriteLine(numValue);
}
// 2. 引用类型的按引用传递情况
private static void changeByRef(ref string numRef)
{
numRef = “new string”;
Console.WriteLine(numRef);
}
#endregion
}

对于按值传递,不管是值类型还是引用类型的按值传递,都是传递实参的
一个拷贝,只是值类型时,此时传递的是实参实例的一个拷贝(也就是值类型值的一个拷贝),而引用类型时,此时传递
的实参引用的副本。对于按引用传递,传递的都是参数地址,也就是实例的指针。