CLR笔记:6.类型和成员基础

来源:互联网 发布:超次元矩阵手游官网 编辑:程序博客网 时间:2024/05/17 03:36

 1.Class的可见性有public和internal两种,public对所有程序集都可见,internal仅对其所在的程序集可见。默认是public的。

  2.友元程序集,

  使用friend assembly可以实现单元测试,而不使用反射技术。

  书上讲的是按照命令行编译。

  我测试用的是vs2005的solution,如下:

  3.成员的可访问性

  成员默认是private的,接口类型的成员都是public的。

  子类重写父类的成员时,原始成员与重写成员要有相同的可访问性——C#的约束;CLR的约束是,重写成员的可访问性不能更低。

  CLR和C#是不一样的,如表:

 

CLR术语C#术语PrivateprivateFamilyprotectedFamily and Assembly不支持AssemblyinternalFamily or Assemblyprotected internalPublicpublic

 

  4.静态类

  static只能用于class,不能用于struct,因为CLR要求值必须实例化,而且不能控制实例化过程。

  C#对静态类的约束:

  静态类必须直接从System.Object派生

  静态类不能实现任何接口

  静态类只能定义静态成员:字段,方法,属性,事件

  静态类不能用作:字段,方法,参数,局部变量。

  在MSIL中,不会为静态类生成ctor,会将其标记为abstract和sealed

  5.部分类

   CLR不支持partial,只是C#的语法。所以某个类型的源码必须使用同一种编程语言

  6.组件,多态和版本控制

  .NET版本号2.7.1.34,包含4个部分:主版本号,次版本号,内部版本号,修订版本号。

   修订版本,向后兼容,改变内部/修订版本号;

   发布新版本,不向后兼容,改变主/次版本号。

  多态中,子类重写父类的虚方法,会引起版本控制问题,即父类发生改变,其版本低于子类版本,会导致子类行为变化。

  C# 5个用于 类/类成员 的 影响组件版本控制 的 关键字:

   abstract:用于类/类成员

   virtual和override:用于成员

   sealed:用于类/类成员。用于成员时,仅用于重写了虚方法的方法。

   new,用于类/类成员/常量/字段

  C#调用虚方法:

   CLR允许类中定义多个"同名方法",仅仅是返回类型不同,IL允许这样做;C#不允许,忽略返回值的类型,相应的用"转换操作符"实现IL中的"同名方法"。

 

   调用方法相应的MSIL:

   一个是call,用来调用静态方法,实例方法和虚方法。必须要指定调用方法的类型(对于静态方法)或者对象(对于实例方法/虚方法),如果在该类型/对象中找不到该方法,会检查其基类来匹配方法。

   另一个是callvirt,用来调用实例方法和虚方法,不能用于调用静态方法。必须要指定调用方法的实例对象,如果这个对象为null,会抛出NullReferenceException异常,这意味着每次调用前都会有额外的null检查,从而比调用call慢一些。

   如下代码所示:

    public sealed class Program
    {
        public Int32 GetFive()
        {
            return 5;
        }

        public static void Main()
        {
            Program p = null;
            Int32 x = p.GetFive();  //在C#中,使用callvirt,会抛出NullReferenceException异常
        }
    }

   在C#编译器中,使用callvirt调用所有实例方法(包括虚方法),使用call调用所有静态方法。对于其他的编译器,这一点不能保证,所以在虚方法和非虚方法之间改动而不重新编译,会产生无法预测的问题。

   C#使用call而不用callvirt调用虚方法的特例:ToString,见下:

    internal class SomeClass
    {
        public override string ToString()
        {
            return base.ToString();
        }
    }

   这时候,生成call的IL代码。因为如果使用callvirt,意味着这时一个虚方法,从而递归执行该方法,直到AppDomain的堆栈溢出。

   在调用值类型定义的方法时,使用call。这是因为,首先,值类型是密封的,从而不存在虚方法;另外,值类型永远不会为null,所以永远不会抛出NullReferenceException异常;再者,如果使用callvirt,就要使用装箱机制,性能会有极大影响。

 

   在设计class的过程中,要尽量少定义虚方法。取代办法:可以定义一组重载方法,经其中最复杂的方法虚拟化,而将所有有用的重载非虚拟化,示例如下:

    public class Set
    {
        private Int32 m_length = 0;

        //这个有用的重载是非虚拟的
        public Int32 Find(Object value)
        {
            return Find(value, 0, m_length);
        }

        //这个有用的重载是非虚拟的
        public Int32 Find(Object value, Int32 startIndex)
        {
            return Find(value, 0, m_length - startIndex);
        }

        //功能最丰富的方法是虚拟的,可以被重写
        public Int32 Find(Object value, Int32 startIndex, Int32 endIndex)
        {
            .//具体实现
        }
    }

   sealed密闭类尽量使用。将sealed改为非密闭的容易,反之困难;性能也快,因为sealed一定是非虚拟的,从而编译器不用考虑其派生类,而虚方法的性能不如非虚方法;因为密闭了,所以安全性和可预测性也就高。

  子类中有父类的方法,会警告,不会报错。这时要使用new关键字,告诉CLR,新旧方法没有任何关系。

  补充:

  1.静态成员是在堆上分配的。CLR在创建TYPE对象的时候,静态数据成员也会创建

  2.实例类中可以有静态成员

原创粉丝点击