CLR Via C# 读书笔记-第6、7、8章(类型和成员基础、常量和字段、方法)

本文深入探讨C#中的类型和成员访问性,指出默认的内部访问修饰符以及CLR的转换。讲解了call和callvirt指令的区别,特别是在虚方法调用中的性能考量。此外,讨论了常量、字段的特性,包括readonly字段的使用。还涉及了方法中的MemberwiseClone()以及构造函数在值类型和密封类中的行为。最后,提到了转换操作符在C#中的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


第六章 类型和成员基础

  • 不显示 定义类型的public或private,会默认为internal
  • 成员访问性定义
    1. 规则C# → CLR
    2. protected → Family
    3. internal → Assembly
    4. 其他都差不多public → Public这样的
  • CLR方法调用指令(这里是原文说的):
    1. call 该 IL 指令可调用静态方法、实例方法和虚方法。用 call 指令调用静态方法,必须指定方法的定义类型。用 call 指令调用实例方法或虚方法,必须指定引用了对象的变量。call指令假定该变量不为null。换言之,变量本身的类型指明了方法的定义类型。如果变量的类型没有定义该方法,就检查基类型来查找匹配方法。call指令经常用于以非虚方式调用虚方法。
    2. callvirt 该 IL 指令可调用实例方法和虚方法,不能调用静态方法。用 callvirt 指令调用实例方法或虚方法,必须指定引用了对象的变量。用 callvirt 指令调用非虚实例方法,变量的类型指明了方法的定义类型。用 callvirt 指令调用虚实例方法,CLR 调查发出调用的对象的实际类型,然后以多态方式调用方法。为了确定类型,发出调用的变量绝不能是 null。换言之,编译这个调用时,JIT 编译器会生成代码来验证变量的值是不是null。如果是, callvirt 指令的执行速度比 call 指令稍慢。注意,即使 callvirt 指令调用的是非虚实例方法,也要执行这种null检查。
下面调用虚方法GetHashCode和非虚方法GetType都是用了`callvirt `,
C#团队认为JIT应当生成代码来检验对象是否为null,这样会使得调用虚方法会慢一点。
.method public hidebysig static void  Main() cil managed {
  .entrypoint
  // Code size 26 (0x1a)
  .maxstack  1
  .locals init (object o)
  IL_0000: call void System.Console::WriteLine() 
  IL_0005: newobj instance void System.Object::.ctor() 
  IL_000a: stloc.0
  IL_000b: ldloc.0
  IL_000c: callvirt instance int32 System.Object::GetHashCode() 
  IL_0011: pop
  IL_0012: ldloc.0
  IL_0013: callvirt instance class System.Type System.Object::GetType() 
  IL_0018: pop
  IL_0019: ret
} // end of method Program::Main
  • 上面我们知道调用虚方法时将会以多态的方式来调用。但是如果类型时密封类sealed,JIT 编译器看到使用密封类型的虚方法调用,就可采用非虚方式调用虚方法,从而生成更高效的代码。之所以能这么做,是因为密封类自然不会有派生类。
  • 安全性和可预测性 派生类可以访问更改基类状态、派生类可以重写和调用基类虚方法。一旦将某个方法、属性或事件设为 virtual,基类就会丧失对它的行为和状态的部分控制权。

第七章 常量和字段

  • 常量会被看成static。并且在运行时常量的值是直接嵌入IL代码中,运行时不会给常量分配内存。
    e.g. 如果A.dll中定义了常量,工程使用了这个常量。如果A修改常量数值,重写生成dll,但是不会得到修改后的常量,必须重写编译。如果需要运行时修改可以定义为static readonly
  • 字段术语
    1. 规则C# → CLR
    2. static → Static
    3. readonly → InitOnly
    4. volatile → Volatile
  • readonly的引用字段,是引用无法改变,而引用的对象是可以改变的

第八章 方法

  • System.Object.MemberwiseClone() 将会分配内存,初始化附加字段,然后将源对象的字节数据复制到新对象中。
  • 值类型和构造方法 下面在new Rectangle(),不会调用Point的无参构造方法,所以最后m_topLeft.m_x会被初始化为0
internal struct Point {
    public Int32 m_x, m_y;
	
    public Point() {
        m_x = m_y = 5;
    }
}

internal sealed class Rectangle {
    public Point m_topLeft, m_bottomRight;

    public Rectangle(){        
    }
}
  • 注意上面的结构定义无参构造方法是会报错的,并且如果要定义构造方法,必须初始化所有字段,看下面
        struct Point
        {
            public readonly int x;
            public readonly int y;

            public Point(int x)
            {
                this = new Point();

                this.x = x;
            }

        }
        static void Main(string[] args)
        {
            Point p = new Point(2);
            Console.WriteLine($"{p.x}-{p.y}");
            // 结果为 2-0
        }
  • static 类名/结构名 这个叫类型构造器
  • 操作符对应CLR方法如下:
转换操作符
  • public static implicit operator Rational(Int32 num) 隐式转换
  • public static explicit operator Int32(Rational r) 显式转换

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值