从C#2.0的角度看.NET 2.0类型系统

C#中所有变量使用之前都必须初始化,否则编译器会在初始化时产生一个编译错误。

每个Windows线程都有一个私有的内存区块,称为栈;也就是说这块内存块不能被其他线程访问(特定条件下除外)。什么条件?
线程的栈主要用于:
  • 保存正在执行中的方法的传入实参值;
  • 保存方法返回是需要跳转的本地代码的地址;
  • 保存对象(但不是全部)。
一个进程通常只有(有时也可以有多个)堆,为其下的所有线程所共享。何时?
堆的优点是容量比栈大得多。为所有线程所共享。
栈的优点是速度比堆快。这主要源于专门访问栈的IL指令,以及访问栈上的元素无需同步。
因此,用堆来存储大对象,用栈存储小对象。。
C#中的值类型的实例使用静态分配(分配在线程栈上),而引用类型的实例则使用动态分配(分配在进程堆中)。

结构、枚举是值类型,委托是
引用类型
如果某个类的实例有个值类型的字段,那么该字段会和类实例保存在一起,即堆中。反过来,如果一个结构的字段是引用类型,那么引用类型的实例还是会保存在堆中。

在C#中,求模运算也可以用于浮点数。
float a = 5.5f;
Console.WriteLine(a % 2.5);
屏幕上将输出0.5。
C#语言提供了checked关键字,可以得到验证所有的类型转换和运算。如果有问题就会引发一个异常。

整形和decimal类型被零除时,会产生DivideByZeroException异常。
浮点型float和double被零除时,会得到无穷大,0/0得到Nan。

C#的递增和递减运算符也可以用于浮点数。
float a = 5.5f;
a++;
此时a = 6.5。

C#中的结构不能从其他类或者结构派生,也不能作为其他类型或结构的基类。
C#的结构可以有多个构造函数,当不允许自定义默认的构造函数(即没有参数的构造函数)。而且编译器要求每一个构造函数都初始化结构的所有字段。
默认构造函数将所有值类型的字段设为零,而将所有引用类型的字段设为空引用。
结构可以有自己的方法。
与类的字段不同,结构的字段不能在声明中显示初始化。
结构的实例常常存储在栈中,因此结构不宜太大。太大的结构最好用类代替。

编译器默认情况下,将枚举的值视为int型整数。因此,枚举定义的这组常数中任意一个值都是一个整数。可以对其进行递增、递减操作。
在默认情况下,枚举的第一个值是0,之后的每一个值都是前一个值加1.当然,也可以手工指定自己想要的值。
你也可以定义其他类型的枚举类型的值:
    enum MakerCHB:byte{Beijing,Zhejiang}
    enum MakerCHL:long{Beijing,Zhejiang}

可以让一个枚举的实例包含多个枚举值。这个概念也称为二进制位标志(binary flag)或者标志枚举(flag enum).
注意,位域枚举都打上了System.Flags attribute标志,该attribute通知CLR和客户端这个枚举用来表示位域而不是标准枚举,这个枚举用来表示位域而不是标准枚举。这个标志会影响ToString()方法的结果。最好能有个实例!
.NET Framework中的System.Threading.ThreadState就是采用了二进制标志的枚举。

C#中String类的实例是不可变。即无法修改它们,它们将保持通过构造函数所赋的值不变。
C#允许定义无转义的字符串字面常量,方法就是在字符串起始的双引号之前加入“@”符号。无转义字符串字面常量具有以下特征:
  • 它将接受所有字符作为字符串的内容,包括“\”反斜杠字符,不过不包括双引号。
  • 它将接受字符串中的所有换行。
C#是String类的实例是不可变的。使得对所有字符串的修改操作均会分配一个新的String实例。如果应用程序要频繁修改字符串或者处理长字符串,那么更好的方法是使用System.Text.StringBuilder类。
通过不安全代码也可以打破字符串实例的不可变特性。

C#允许用delegate关键字创建一种特殊的类,我们称这种类为委托类。委托类的实例称为委托对象。
委托对象是一种指向一个或多个方法(静态或非静态)的引用。我们可以像调用方法那样“调用”委托对象,这其实就是调用委托对象所引用的方法。注意这些方法的调用是在调用委托的方法所在的线程中完成的,即同步调用。
委托变量所能引用的方法只能是其签名式与委托类声明中所提供的签名式一致的方法。
delegate void Deleg1();
delegate string Deleg2(string s);
delegate void f1()
{
}

static string f2(string s)
{
}

Deleg1 d1 
= new Deleg1(f1);   // right
Deleg2 d2 = new Deleg2(f2);   // right
Deleg2 d3 = new Deleg2(f1);   // error
C# 2.0编译器引入在创建委托变量时推测其类型的功能。于是就可以直接将一个方法赋给隐式创建的对象。如
Deleg1 d1 = f1;
Deleg2 d2 = f2;
但是,其编译生成的IL代码,跟前面的是一样的,它仍旧调用了Deleg1和Deleg2委托类的构造函数。
可以用一个委托对象应用多个具有相同签名式的方法(静态或非静态)。这种情况下调用委托对象就会在调用对象的线程中顺序执行所有的方法。调用方法的顺序就是方法添加到委托对象中的顺序,每个方法的参数都是一样的。
如果委托的签名具有返回值,那么引用多个方法时只有最后一个调用的方法的返回值才作为委托对象的返回值返回。
可以用GetInvocationList()获取该委托对象中的委托列表。

可空类型System.Nullable<T>等价于T?。
    即int? i = null;和Nullable<int> i = null;是等价的。
不过,编译器会阻止从可空类型到原始类型的隐式转换。此外,不事先测试而进行可空类型到原始类型的显示转换也是危险的,因为,这可能会引发InvalidOperationException异常。

C# 2.0允许将类、结构和接口的声明分散到多个源文件中。我们称这一特性命名为部分类型。注意,委托类或者枚举类是不能声明在多个源文件中的。
同一个类型各个不同的部分定义前面必须都加partial关键字。而且,如果该类型是泛型的,那么类型参数的定义也必须出现在每个部分的部分定义上。每个部分定义上类型参数的名称和位置必须完全一致。即在每个部分什么的class、struct或interface关键字之前加partial关键字。而且必须属于同一个命名空间。
原文地址:https://www.cnblogs.com/adaiye/p/ValueType.html