1、常量
在C#中以下类型是基类型,可用于定义常量:Boolen、Char、Byte、SByte、Int16、Uint16 、Int32、UInt32、Int64、UInt64、Single、Double、Decimal和 String。
C#也可以定义非基元类型的常量变量,前提是把值设为null:
using System; public sealed class SomeType { // SomeType is not a primitive type but C# does allow // a constant variable of this type to be set to ‘null‘. public const SomeType Empty = null; }
因为常量不会被改变,所以总是被视为类型定义的一部分,也就是说它被视为静态成员,而不是实例成员。
常量定义后从不变化的特性有以下需要注意的:
1、因为不会被改变,代码引用常量符号时,编译器在定义常量的程序集的元数据中查找该符号,提取常量的值,将值嵌入生产的IL代码中。
2、因为常量值内嵌到IL代码中,所以在运行时不需要分配内存。
3、又因为常量不需要分配内存,所以不能获取常量的地址,也不能以传递引用的方式传递常量。
这些限制意味着不能很好地支持跨程序集的版本控制。
2、字段
字段是一种数据成员,其中容纳了一个值类型的实例或者对一个引用类型的引用。
CLR 支持类型字段和 实例字段。
-
类型字段:也就是静态字段,它属于类型对象,容纳字段数据所需的动态内存是在类型对象中分配的,而类型对象是在类型加载到一个AppDomain时创建的。而类型加载到AppDomain中通常是在引用了该类型的任何方法首次进行JIT 编译的时候。
-
实例字段:非静态字段,它属于类型对象的实例,在一个AppDomain中,类型对象只有一个,而由它构造的实例会有好多。如果是实例字段,容纳字段数据所需的动态内存是在构造类型的实例时分配的。
下面是字段修饰符的详细说明:
3、方法
3.1 实例构造器和类(引用类型)
构造器是将类型的实例初始化为良好状态的特殊方法。
创建引用类型的实例时:
-
首先 为实例的数据字段分配内存
-
然后 初始化对象的附加字段(类型对象指针和同步块索引)
-
最后 调用类型的实例构造器来设置对象的初始化状态
引用类型的实例构造器又以下特点:
-
构造引用类型的对象时,没有被构造器显示重写的所有字段都默认为0或者null。
-
由于永远不能继承实例构造器,所以实例构造器不能使用以下修饰符:virtual, new, override, sealed,or abstract。
-
如果没有显示的定义任何构造器,C#编译器将定义一个默认构造器,在它的实现中,只是调用了基类的无参构造器。
-
一个类型可以定义多个实例构造器,每个构造器必须有不同的签名,而且每个都可以有不同的访问性。
-
类的实例构造器在访问从基类继承的任何字段之前,必须先调用基类的构造器。
-
极少数时候可以在不调用实例构造器的前提下创建类型的实例。一个是调用Object 的 MemberwiseClone方法。另一个是是反序列化代码使用System.Runtime.Serialization.FormatterServices 类型的GetUninitializedObject 或者GetSageUninitializedObject 方法为对象分配内存,期间不会调用一个构造器。
3.2 实例构造器和结构(值类型)
CLR 总是允许创建值类型的实例,并且没有办法阻止值类型的实例化。
值类型构造器的特点:
-
值类型并不需要定义构造器,C#编译器不会为值类型内联默认的无参构造器。
-
但CLR确实允许值类型定义构造器,但是必须显示调用才会执行。即便值类型提供了构造器,许多编程器也永远不会自动生成这样的代码。
-
还有C#编译器不允许值类型定义无参构造器。
-
为了生成”可验证“代码,在访问值类型的任何字段之前,都需要对全部字段进行赋值。
// C# allows value types to have constructors that take parameters. public SomeValType(Int32 x) { // Looks strange but compiles fine and initializes all fields to 0/null. this = new SomeValType(); m_x = x; // Overwrite m_x‘s 0 with x // Notice that m_y was initialized to 0. }
-
在值类型构造器中,this 代表值类型本身的一个实例,用new 创建的值类型的一个实例可以赋值给this。在new 的过程中,会将所有字段设置为零。
3.3 类型构造器
CLR 支持类型构造器,也称为静态构造器(static constructors )、类型构造器(class constructors )、或者类型初始化器(type initializers )。类型构造器可应用于接口(C#编译器不允许)、引用类型、和值类型。
类型构造器特点:
-
实例构造器是设置类型实例的初始化状态。对应地,类型构造器的作用是设置类型的初始化状态。
-
类型默认没有构造器,如果定义,也只能定义一个。
-
类型构造器永远没有参数。
internal sealed class SomeRefType { static SomeRefType() { // This executes the first time a SomeRefType is accessed. } } internal struct SomeValType { // C# does allow value types to define parameterless type constructors. static SomeValType() { // This executes the first time a SomeValType is accessed. } }
-
虽然C#不允许值类型为它的实例字段使用内联字段初始化语法,但可以为静态字段使用。换句话说,如果将下面定义的SomeType类型从class 改为struct ,那么代码也能编译通过。
internal sealed class SomeType { private static Int32 s_x = 5; }
生成上述代码时,编译器自动为SomeType 生成一个类型构造器:
internal sealed class SomeType { private static Int32 s_x; static SomeType() { s_x = 5; } }
-
类型构造器不应调用基类型的类型构造器。因为类型不可能有静态字段是从基类型分享或继承的。
4、扩展方法
学习扩展方法 的做好方式就是从例子中学习。扩展方法要定义在静态类中,并且在方法的第一个参数前 添加 this 关键字,第一个参数的类型和当前用于调用方法的那个表达式的类型匹配。
当编译器看到上面的代码时,就会先检查 StringBuilder 类或者它的任何基类是否提供了获取单个Char 参数、名为 IndexOf 的一个实例方法。如果是,就生成 IL 代码来调用它。如果灭有找到匹配的实例方法,就继续检查是否有任何静态类定义了名为 IndexOf 的静态方法,方法的第一个参数的类型和当前调用方法的那个表达式的类型匹配,而其该类型必须用 this 关键字标识。
扩展方法的原则: (这里只提几个重要的原则)
-
C# 只支持扩展方法,不支持扩展属性、扩展事件、扩展操作符等。
-
扩展方法(第一个参数前面有 this 的方法) 必须在非泛型的静态类型中声明。类型名没有限制,随便叫什么名字。至少有一个参数,而且只有第一个参数能用 this 关键字标记。
-
多个静态类可以定义相同的扩展方法。扩展方法所在的静态类必须具有文件作用域(不能嵌套在另一个类中)。
-
用一个扩展方法扩展一个类型时,同时也扩展了派生类型。
注意: 由于扩展方法实际是对一个静态方法的调用,所以CLR 不会生成代码对调用方法的表达式的值进行 null 值检查(不保证它非空)。扩展方法式 Microsoft 的 LINQ 技术的基础。
ExtensionAttribute 类 在C# 中一旦用this 关键字 标记了某个静态方法的第一个参数,编译器就会在内部向该方法应用一个定制特性。该特性会在最终生成的文件的数据中持久性的存储下来。 除此之外,任何静态类只要包含至少一个扩展方法,它的元数据中也会应用这个特性。类似地,任何程序集只要包含了至少一个符合上述特点的静态类,它的元数据中也会应用这个特性。