C#中的继承机制

363 阅读7分钟

写在前面

在编程语言中 继承这一特性是绝大多数面向对象语言都支持的,主要用来简化开发流程,减少代码的重复 使得程序结构更紧凑。

笔者近段时间在看一些 C#相关的网课,刚好也学到了继承这一块,固 将所学的知识点总结一下,由于太过基础;代码示例会比较多

在C#中如何实现继承

和 Java不同,C#的继承不是使用 extends关键字而是以DerivedClass:BaseClass的方式来实现,具体代码如下:

// 创建基类
class BaseClass
    {
       
    }
// 创建派生类,并使用 ":" 来继承基类
class DerivedClass : BaseClass
    {
       
    }

不得不说,使用这种方式要显得更优雅一些。

当然,同大多数高级语言一样,C#也是单继承多实现,并不支持多继承。

实现继承后

基类中的属性和方法都会被派生类所继承

class BaseClass
{
    // 基类中的私有属性
    private string baseData;

    public void BaseMethod()
    {
        Console.WriteLine("基类中的方法");
    }
}


// 测试类
class Test
{
    public static void Main(String[] args)
    {
        DerivedClass dc = new DerivedClass();
        // 派生类拥有基类的方法
        dc.BaseMethod();
        dc.baseData = "派生类无法操作基类中的私有属性";
    }
}

因为 private修饰符的缘故,派生类虽然继承了基类中的全部方法和属性,但并没有权限去对基类中私有的属性进行操作。

如果想让派生类能够操作基类的私有属性,我们可以在基类中给私有属性添加 get set方法,也可以将private修饰符修改为 public或者protected

class BaseClass
    {
    	// 将 private修改为 protected,这样派生类就能够操作基类中受保护的属性了
		protected string baseData;
    }
class BaseClass
{
    private string baseData;
     // 为基类中私有的属性添加 get set方法
    public string BaseData { get;set; }
}

派生类中的构造方法

我们在实例化派生类对象时,默认的会调用一次基类的构造函数

正常来说我们不去动基类,它会有一个默认的空构造,我们也可以显式的把它写出来;在空构造中写一些测试语句

public BaseClass()
{
    Console.WriteLine("BaseTest");
}

此时我们在测试类实例化基类对象,就会发现输出语句出现在了控制台中。


在开发中,派生类继承自基类后 我们往往都需要在派生类中为其定义自己的属性和方法;但你在书写派生类的构造方法时会发现没有办法使用 this.基类属性;的方式在构造方法中为基类的属性赋值。

这也很好理解,因为 this指的是派生类本身,为了和基类区分开来 C#提供了 base关键字,这一关键字类似 Java中的 super,用来指代父类。

class DerivedClass : BaseClass
{
    // 派生类中的私有属性
    private string derivedData;
    // 派生类的构造方法
    public DerivedClass(string derivedData, string baseData)
    {
        this.derivedData = derivedData;
        // 使用 base关键字调用基类的私有属性
        base.BaseData = baseData;
    }
}

可以看到,我们在上文中定义的 基类属性 为 private string baseData;而在派生类的构造方法中通过 base关键字调用的属性却首字母大写了。

这并不是因为疏忽或者说什么不区分大小写,而是因为我们在基类中书写 get set时需要将属性的首字母大写,我们也是通过 get set来操作的基类私有属性,所以调用时也是大写。

如果你将 get set拿掉,并把 private修改为 protected或者 public,再通过 base关键字来调用就是小写了。


在派生类的构造方法中,除了使用base关键字直接操作基类的非私有属性外 我们还可以通过另一种方式来 在实例化派生类对象时为继承自基类的属性赋值。

但使用这种方法 我们需要先去父类中为父类生成一个有参的构造函数

public BaseClass(string baseData)
{
    this.baseData = baseData;
}

此时需要注意,当基类中存在有参 且未显式的定义无参构造时,我们的派生类中的有参构造就会报错。

这是因为当我们在基类中定义有参构造后,我们就必须在派生类中 为基类有参构造中的参数提供实参和形参

但像之前一样使用 base关键字就行不通了,我们需要使用另一种方式

public DerivedClass(string baseData, string derivedData):base(baseData) // 使用 ":base(基类参数)"的方式在派生类中调用基类的构造函数
{
    this.derivedData = derivedData;
}

在派生类构造函数形参后面加上 :base(基类参数) 后,我们使用该构造函数实例化派生类对象时 就不得不传入对应的基类参数了

DerivedClass dc = new DerivedClass("baseData","derivedData");

这样就实现了,在派生类对象实例化时,为继承自基类的属性赋值这一操作,实际上是一个调用基类有参构造的过程。

派生类重写父类方法

派生类重写父类的方法有两种方式,一种是使用 虚方法,而另一种则是通过在派生类中书写重名方法来隐藏基类的方法。

虚方法

C#为我们提供了 virtual关键字和 override关键字使我们能够在派生类中重写基类的方法,这种重写的方式也被叫作是虚方法

class BaseClass
{
    // 使用 virtual关键字声明基类的方法
    public virtual void BaseMethod()
    {
        Console.WriteLine("基类中的方法");
    }
}



class DerivedClass : BaseClass
{
	// 在派生类中使用 override关键字,重写继承自基类的 BaseMethod();方法
    public override void BaseMethod()
    {
        Console.WriteLine("派生类重写了 BaseMethod方法");
    }
}

重写过派生类继承自基类的方法后,我们再通过派生类对象实例调用 BaseMethod();时,就不再会去调用继承来的基类方法了。

隐藏方法

隐藏方法相对于虚方法来说更为简单,只需要在派生类中再书写一次继承来的方法即可,但为了规范我们会使用 new关键字来表示隐藏了继承自基类的方法。

class DerivedClass : BaseClass
{
    // 使用 new关键字表示隐藏继承自基类的同名方法,达到重写父类方法的目的。
    public new void BaseMethod()
    {
        Console.WriteLine("派生类重写了 BaseMethod方法");
    }
}

使用隐藏方法来重写继承自基类的方法时,我们无需对基类进行操作。

二者的区别

同样是重写继承自基类的方法,虚方法和隐藏方法有何不同?

我们不妨在基类中书写两个方法,并在派生类中一个使用虚方法重写,另一个使用隐藏方法重写来测试一下。

class BaseClass
{
    // 使用 virtual关键字声明基类的方法
    public virtual void BaseMethod()
    {
        Console.WriteLine("基类中的方法");
    }
    public void BaseMethod1()
    {
        Console.WriteLine("基类中的方法1");
    }
}


class DerivedClass : BaseClass
{
    // 使用 虚方法重写继承自基类的方法
    public override void BaseMethod()
    {
        Console.WriteLine("派生类重写了 BaseMethod方法");
    }
    // 使用 new关键字表示隐藏继承自基类的同名方法,达到重写父类方法的目的。
    public new void BaseMethod1()
    {
        Console.WriteLine("派生类重写了 BaseMethod1方法");
    }
}

调用后发现并无区别

但我们都知道,继承中有一个操作 即 使用基类声明一个对象,再使用派生类来实例化这个对象。

BaseClass bc = new DerivedClass();

就像这样。这样操作创建的对象实例 实际上指向的是右边的那个,但这样创建的实例也有它的特殊性。

其一是,虽然构造它的是派生类,但派生类独有的属性和方法它是无法调用的

那么重写的方法呢?我们不妨测试一下

如图所示,当我们使用这类特殊构造的对象调用重写的方法时,派生类中通过隐藏方法重写的 BaseMethod1();就没能被调用;而是调用了基类中的 BaseMethod1();,但是通过 virtual关键字和 override关键字重写的派生类方法则被调用了。

这就是二者的区别


放松一下眼睛

原图P站地址

画师主页