常量、字段、和方法

岁酱吖の 提交于 2019-11-30 19:51:39

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 关键字 标记了某个静态方法的第一个参数,编译器就会在内部向该方法应用一个定制特性。该特性会在最终生成的文件的数据中持久性的存储下来。 除此之外,任何静态类只要包含至少一个扩展方法,它的元数据中也会应用这个特性。类似地,任何程序集只要包含了至少一个符合上述特点的静态类,它的元数据中也会应用这个特性。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!