25. C# -- 继承(override,virtual,sealed)

继承(加上封装和多态性)是面向对象的编程的三个主要特性(“支柱”)之一。继承用于创建可重用、扩展和修改在其他类中定义的行为的新类。其成员被继承的类称为“基类”,继承这些成员的类称为“派生类”。

定义一个类从其他类派生时,派生类隐式获得基类的除构造函数和析构函数以外的所有成员。因此,派生类可以重用基类中的代码而无需重新实现这些代码。可以在派生类中添加更多成员。派生类以这种方式扩展基类的功能。

C#中提供了类的继承机制,但只支持单继承,而不支持多继承,即在C#中一次只允许继承一个类,不能同时继承多个类。

派生类对基类成员的访问:

派生类可以访问基类的公共成员、受保护成员、内部成员和受保护内部成员。即使派生类继承基类的私有成员,仍不能访问这些成员。但是,所有这些私有成员在派生类中仍然存在,且执行与基类自身中相同的工作。例如,假定一个受保护基类方法访问私有字段。要使继承的基类方法正常工作,派生类中必须有该字段。

为了解决基类成员访问问题,C#还提供了另外一种可访问行:protected。只有子类(派生类)才能访问protected成员,基类和外部代码都不能访问protected成员。

除了成员的保护级别外,还可以为成员定义其继承的行为。基类的成员可以是虚拟的,成员可以由继承它的类重写。子类(派生类)可以提供成员的其它执行代码,这种执行代码不会删除原来的代码,仍可以在类中访问原来的代码,但外部代码不能访问他们。如果没有提供其它执行方式,外部代码就直接访问基类中成员的执行代码。

另外,基类还可以定义为抽象类。抽象类不能直接实例化,要使用抽象类就必须继承这个类,然后再实例化。

base 表示当前对象基类的实例(使用base关键字可以调用基类的成员),this表示当前类的实例

在静态方法中不可以使用base和this关键字。

派生类会继承基类所有的成员,但是构造函数和析构函数不会被继承。

注意:如果派生类的方法和基类的方法同名,则基类中的方法将会被隐藏。如果需要隐藏则可以使用关键字new来隐藏如果不写new关键字,默认处理为隐藏。虽然基类中同名的方法被隐藏了,但是还是可以通过base关键字来调用。

例1:简单继承

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
    class Program
{
        static void Main(string[] args)
{
            //实例化Test2子类
            Test2 ts2 = new Test2();
            //可以调用基类的Print方法
ts2.Print();
            Console.WriteLine();
            //调用自己的Show方法
ts2.Show();
            Console.Read();
}
}
    //基类
    class Test
{
        int num1, num2;
        public Test()
{
num1 = 1;
num2 = 2;
}
        public void Print()
{
            Console.WriteLine("------------------------------");
            Console.WriteLine("{0} + {1} = {2}", num1, num2, num1 + num2);
            Console.WriteLine("我是基类的Print方法\n------------------------------");
}
}
    //子类
    class Test2 : Test
{
        //如果子类方法的方法名和基类的方法名相同时,系统将隐藏基类同名方法,自动调用子类的同名方法
        public void Show()
{
            Console.WriteLine("------------------------------");
            //派生类会继承基类所有的成员,但是不能显示调用基类的是有成员
            //在派生类中不可以调用基类的是有成员,如num1,num2,但是可以实现调用基类方法
Print();
            Console.WriteLine("我是子类的Show方法\n------------------------------");
}
}
}

上述代码运行结果如下:

继承(C) - Steven - Steven blog


C#继承中的重写 (override + sealed)

◆virtual 用在基类中,指定一个虚方法(属性),表示这个方法(属性)可以重写。

◆override 用在派生类中,表示对基类虚方法(属性)的重写。

不能重写非虚方法或静态方法。重写的基方法必须是 virtual、abstract 或 override 的。为什么 override 也可以重写呢?因为基类中的 override 实际上是对基类的基类进行的重写,由于继承可传递,所以也可以对基类中 override 的方法进行重写。

◆override 声明不能更改 virtual 方法的可访问性。override 方法和 virtual 方法必须具有相同的访问级别修饰符。
不能使用修饰符 new、static、virtual 或 abstract 来修改 override 方法。

◆重写属性声明必须指定与继承属性完全相同的访问修饰符、类型和名称,并且被重写的属性必须是 virtual、abstract 或 override 的。

标记允许被重写,修饰。静态方法中不允许使用virtual关键字。成员变量允许使用virtual关键字。
 属性可以被声明为虚属性(使用virtual关键字)

重写基类的方法(重写同名的方法中有virtual关键字的方法)

重写和隐藏的区别:隐藏(new关键字)是给子类的同名方法分配新的内存空间,重写(override关键字)是子类的同名方法放在基类同名方法的原来所在位置,基类的同名方法位置向后移。

属性也可以重写。

virtual关键字和override关键字是成对出现的,否则是语法错误。

派生类可以通过将重写声明为 sealed 来停止虚拟继承。这需要在类成员声明中的 override 关键字前面放置 sealed 关键字。

在子类中重写基类中的虚方法时,可以使用base关键字调用基类中的虚方法。

 例2:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
    class Program
{
        static void Main(string[] args)
{
            //实例化Test2子类
            Test2 ts2 = new Test2();
            //可以调用基类的Print方法
ts2.Print();
            Console.WriteLine();
            //调用自己的Show方法
ts2.Show();
            Test3 ts3 = new Test3();
ts3.Print();
ts3.Show();
            Console.Read();
}
}
    class Test
{
        public virtual void Print()
{
            Console.WriteLine("I am Test Print()");
}
        public virtual void Show()
{
            Console.WriteLine("I am Test Show()");
}
}
    class Test2 : Test
{
        public override void Print()
{
            Console.WriteLine("I am test2 Print()");
            //使用base关键可以在子类中访问基类同名的方法,
            base.Print();
}
        public sealed override void Show()
{
            Console.WriteLine("I am test2 Show()");
}
}
    class Test3 : Test2
{
        public override void Print()
{
            Console.WriteLine("I am test3  Print()");
            //使用base关键可以在子类中访问基类同名的方法,
            base.Print();
}
        //因为基类使用了sealed,所以不能派生类中继承show()方法
        //public override void Show()
        //{
        //    Console.WriteLine("I am test3 show()");
        //}
}
}

结果:

wKiom1VcOcTSUVVNAABzu0uZBMs520.jpg

父类的引用指向子类的实例:(virtual + override

父类的引用指向子类的实例:(调用的是子类的方法)

父类的引用只认识父类的方法,不认识子类的新方法,可以用来调用被子类覆盖的父类的方法。

父类的引用依然到父类方法位置去调用,如果基类方法被声明为virtual并且在子类中被override,结果访问到的是被子类override的方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
    class Program
{
        static void Main(string[] args)
{
            //父类的引用指向子类的实例
            Test ts = new Test2();
ts.Print();
            Console.Read();
}
}
    class Test
{
        public virtual void Print()
{
            Console.WriteLine("I am test Print()");
}
}
    class Test2 : Test
{
        public override void Print()
{
            Console.WriteLine("I am test2 Print()");
}
}
}


结果:

I am test2  Print()


禁止进一步派生:密封类 (sealed class

类可以将自身或其成员声明为 sealed,从而禁止其他类从该类自身或其任何成员继承。(无法被继承)

sealed关键字:

在类声明中使用sealed可防止其它类继承此类;在方法声明中使用sealed修饰符可防止扩充类重写此方法。相当于Java中的final类和final方法

密封类:

密封类在声明中使用sealed 修饰符,这样就可以防止该类被其它类继承。如果试图将一个密封类作为其它类的基类,C#将提示出错。

在哪些场合下使用密封类呢?实际上,密封类中不可能有派生类。如果密封类实例中存在虚成员函数,该成员函数可以转化为非虚的,函数修饰符virtual 不再生效。

让我们看下面的例子:

abstract class AbstractClass
{
      public abstract void Method( ) ;
}
sealed class SealedClass: AbstractClass
{
       public override void Method( )
        { //... }
}
//如果我们尝试写下面的代码

class OtherClass: SealedClass
{
}
//C#会指出这个错误,告诉你SealedClass 是一个密封类,不能试图从SealedClass 中派生任何类。


关于继承中的构造函数:

子类中默认的构造函数调用基类默认构造函数 :base()
       public 类名():base()
       {
       }
       子类调用基类带参的构造函数
       public 类名(参数):base(参数)
       {
       }

参数:base 表示基类,this表示当前类


参考:

http://blog.163.com/hweibin126@126/blog/static/17044246920101012631818/ 

http://www.cnblogs.com/charley_yang/archive/2010/09/11/1824025.html 

你可能感兴趣的:(继承,C#)