c#之继承

一:继承的分类
从c#中的继承可以分为两种(其他面向对象的语言貌似也是这样)
实现继承(不能实现多重继承,子类只能从一个父类派生)
接口继承(可以从多个接口派生)
结构是值类型的,不支持实现继承,支持接口继承,可以派生于多个接口

二:修饰符

public 任何代码均可访问
protected 仅派生类型可访问,实例对象不可访问
internal 程序集内部可访问
private 只能在类内部访问
protected internal 程序集内部派生类中
new 用相同的签名覆盖基类中的成员
static 成员不在类的具体实例上执行
virtual 成员可以由派生类重写
abstract 只定义成员的签名,没有实现代码
override 该成员重写了基类中的相同签名的virtual成员,并允许被再次重写
sealed 该成员重写了基类中的相同签名的virtual成员,并不允许被再次重写

 

三:
子类拥有父类的所有子类可见成员
这也是设计子类的目的之一,为了扩展父类的功能

四:重写
方法只要声明为virtual,就可以被子类中签名相同的方法重写(override)
当然子类也可以不重写这个方法
成员字段和静态函数都不能被声明为virtual
一个方法被修饰成override,这个方法仍旧可以被它的子类重写

五:覆盖
用new关键字可以隐藏基类的方法,字段
这个感觉没什么好说的

综合例子一(看输出结果之前,希望你能仔细想以想)

    public class baseClass
    {
        public void Methord()
        {
            Console.WriteLine("methord from base");
        }
        public virtual void Methord2()
        {
            Console.WriteLine("virtual methord from base");
        }
    }
    public class sonClass : baseClass
    {
        public new void Methord()
        {
            Console.WriteLine("methord form son");
        }
        public override void Methord2()
        {
            Console.WriteLine("override methord from son");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            baseClass b = new baseClass();
            b.Methord();
            b.Methord2();
            Console.WriteLine();

            sonClass s = new sonClass();
            s.Methord();
            s.Methord2();
            Console.WriteLine();

            baseClass b2 = new sonClass();
            b2.Methord();
            b2.Methord2();
            Console.WriteLine();

            sonClass s2 = (sonClass)b2;
            s2.Methord();
            s2.Methord2();
            Console.WriteLine();
            
            Console.ReadKey();
        }
    }

输出结果为:
image 
很多面试题中都有类似的题
我做个总结
一般情况下变量的类型是什么,该变量就拥有什么类型的成员
即使像下面这种情况也不例外
baseClass b2 = new sonClass();
b2的成员是baseClass中的成员
b2与sonClass中的成员无关
只有一种情况除外
当父类中的virtual方法已经被子类中的方法override过之后(new重写过之后是不行的)
类似这种情况baseClass b2 = new sonClass();
b2拥有的是重写过的方法成员
具体的原理以后有机会分析一下IL代码
那么我们总结一下这个现象
每个类型都有自己的类型成员表,虚方法成员是动态绑定的,运行时动态覆盖

综合例子二

    public class A
    {
        public virtual void Fun1(int i)
        {
            Console.WriteLine(i);
        }

        public void Fun2(A a)
        {
            a.Fun1(1);
            Fun1(5);
        }
    }
    public class B : A
    {
        public override void Fun1(int i)
        {
            base.Fun1(i + 1);
        }

        public static void Main()
        {
            B b = new B();
            A a = new A();
            a.Fun2(b);
            b.Fun2(a);
            Console.ReadKey();
        }
    }

输出为
image 
就不多解释了

六:通过base关键字获取基类的成员
看个比较特殊的例子

    public class baseClass
    {
        public virtual void Methord2()
        {
            Console.WriteLine("virtual methord from base");
        }
    }
    public class sonClass : baseClass
    {
        public override void Methord2()
        {
            base.Methord2();
            Console.WriteLine("override methord from son");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            baseClass b2 = new sonClass();
            b2.Methord2();
            
            Console.ReadKey();
        }
    }

 

输出为:
image 
由此可见重写方法是可以通过base关键字调用被重写的方法的
基类的成员表在重写方法中是可见的

七:抽象类和抽象方法(abstract)
抽象类不能实例化
抽象方法没有执行代码
如果类包含抽象方法,那么该类也必须声明为abstract
当然一个声明为abstract的类可以包含实例方法
抽象方法与虚方法类似,也是运行时动态绑定的

八:密封类和密封方法(sealed)
密封类不能被继承
sealed关键字只能修饰重写(override)方法
密封方法不能被重写
但是可以通过new关键字覆盖它
除非特殊情况最好少用这个关键字

九:继承关系中的构造函数
初始化一个类的实例的具体步骤是
1:初始化该类的字段
2:初始化基类的字段
3:初始化基类的构造函数
4:初始化该类的构造函数
可以通过base关键字制定初始化基类中的哪个构造函数

再看个综合例子,你觉得应该输出什么

    class A 
    { 
          public A() 
          { 
                PrintFields(); 
          } 
          public virtual void PrintFields(){} 
      }
    class B : A
    {
        int x = 1;
        int y;
        public B()
        {
            y = -1;
            PrintFields();
        }
        public override void PrintFields()
        {
            Console.WriteLine("x={0},y={1}", x, y);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            A b = new B();
            Console.ReadKey();
        }
    }

输出为:
image 
就不多解释了

十:接口继承
接口继承和实现继承其实差不多
做几点说明:
1.一个类可以实现多个接口
2.不允许提供接口中任何成员的实现方式
3.接口只能包含方法,属性,所引器和事件,不允许包含运算符重载
4.不能实例化接口,因此接口不能有构造函数
5.不能声明成员修饰符,接口成员总是公共的,也不能声明成员为虚拟的或者静态的,这些是具体实现的时候做的事情


做此文得到了郑州的Xuefly的支持,在此表示感谢

posted @ 2009-05-29 09:52  liulun  阅读(900)  评论(1编辑  收藏  举报