为了账号安全,请及时绑定邮箱和手机立即绑定

来聊一聊C# 的继承

标签:
C#


在编程中也沿用了继承的概念,在面向对象编程中,如C++和C#中都有类的继承。 继承(加上封装和多态性)是面向对象的编程的三个主要特性(也称为“支柱”)之一。 继承用于创建可重用、扩展和修改在其他类中定义的行为的新类。 其成员被继承的类称为“基类”,继承这些成员的类称为“派生类”。 派生类只能有一个直接基类。 但是,继承是可传递的。 如果 ClassB 派生出 ClassC,ClassA 派生出 ClassB,则 ClassC 会继承 ClassB 和 ClassA 中声明的成员。


v写在前面

无论是其他社区还是园子里,关于继承类的帖子或者文章已经可以说是数不胜数了,关于继承类的话题也是旧调重弹了。写这篇博文只是为了加深一下自己对继承的理解,所谓读书百遍其意自见嘛。这篇博文中部分内容来自我的读书(C#高级编程从开始接触C#到现在我已经是第五遍读了,每次读都有不一样的收获。)笔记,和一些自己的理解。


vC#继承

1.实现继承 

如果要声明派生自另一个类的一个类,就可以使用下面的语法:

class LearningClass : BasicClass{ 
       
}

如果类(或结构)也派生自接口,则用逗号分隔列表中的基类和接口:

class LearningClass : BasicClass, BasicInterface, BasicInterface2{    

}

对于结构,语法如下:

struct LearningStruct : BasicInterface, BasicInterface2{
    
}

使用结构的一个限制是结构不支持继承,但每个结 构都自动派生自system,ValueType。实际上还应更仔细一些:不能编码实现类型层次的结构,但结 构可以实现接口。换言之,结构并不支持实现继承,但支持接口继承。事实上,定义结构和类可以 总结为:

  • 结构总是派生自system。ValueType,它们还可以派生自任意多个接口。

  • 类总是派生自用户选择的另一个类,它们还可以派生自任意多个接口。

在类定义中没有指定基类,钭编译器就假定system.Object是基类。因此下面的两段代码生成相同的结果:

class LearningClass : Object{    

}    
class LearningClass{    

}

2.虚方法virtual: 

有时候我们继承一个基类的某些方法希望能重新定义这个方法,我们可以把一个基类函数声明virtual,就可以在任何派生类中重写这个函数:


public class BasicClass
    {
        public virtual string GetMessage()
        {
            return "Basic class message";
        }
    }


C#中虚函数的概念与标准00P的概念相同:可以在派生类中重写虚函数。在调用方法时,会调用该类对象的合适方法。在C#中,函数在默认情况下不是虚拟的,但(除了构造函数以外)可以显 式地声明为virtual。这遵循C++的方式,即从性能的角度来看,除非显式指定,否则函数就不是虚 拟的。而在Java中,所有的函数都是虚拟的。但C#的语法与C++的语法不同,因为C#要求在派生 类的函数重写另一个函数时,要使用override关键字显式声明:


class LearningClass : BasicClass
    {
        public override string GetMessage()
        {
            return "Learning class message";
        }
    }


重写方法的语法避免了C++中很容易发生的潜在运行错误:当派生类的方法签名无意中与基类 版本略有差别时,该方法就不能重写基类的方法:在C#中,这会出现一个编译错误,因为编译器会 认为函数已标记为override,但没有重写其基类的方法。

成员字段和静态函数都不能声明为virtual,因为这个概念只对类中的实例函数成员有意义。

3.隐藏方法: 

如果签名相同的方法在基类和派生类中都进行了声明,但该方法没有分别声明为virtual和 override,派生类方法就会隐藏基类方法。

在大多数清况下,是要重写方法,而不是隐藏方法,因为隐藏方法会造成对于给定类的实例调用 错误方法的危险。但是,如下面的例子所示,C#语法可以确保开发人员在编译时收到这个潜在错误的 警告,从而使隐藏方弦【如果这确实是用户的本匐更加安全。这也是类库开发人员得到的版本方面的 · 好处


此时,编译时系统会发出警告。在C#中,要隐藏一个方法应使用new关键字声明,如下所示:


public class LearningClass : BasicClass
    {
        public new string GetMessage()
        {
            return "Learning class message";
        }
    }


4.调用函数的基类版本: 

C#有一种特殊的语法用于从派生类中调用方法的基类版本:base.()。例如,假定 派生类中的一个方法要返回基类的方法20%的返回值,就可以使用下面的语法:


    public class BasicClass
    {
        public virtual float GetPrice()
        {
            return 1.5f;
        }
    }
    public class LearningClass : BasicClass
    {
        public float GetPrice()
        {
            return base.GetPrice() * 0.2f;
        }
    }

ps:可以使用base.()语法调用基类中的任何方法,不必从同一个方法的重载 中调用它。

5.抽象类和抽象函数: 

C#允许把类和函数声明为abstract。抽象类不能实例化,而抽象函数不能直接实现,必须在非抽 象的派生类中重写。显然,抽象函数本身也是虚拟的(尽管也不需要提供virtual关键字,实际上,如 果提供了该关键字,就会产生一个语法错误。如果类包含抽象函数,则该类也是抽象的,也必须声 明为抽象的:


    public abstract class BasicClass
    {
        public abstract float GetPrice();
    }
    public class LearningClass : BasicClass
    {
        public override float GetPrice()
        {
            return 0.2f;
        }
    }

6.密封类和密封方法: 

C#允许把类和方法声明为sealed。对于类,这表示不能继承该类;对于方法,这表示不能重写该方法。

在把类或方法标记为sealed时,最可能的情形是:如果要对库、类或自己编写的其他类作用域 之外的类或方法进行操作,则重写某些功能会导致代码混乱。也可以因商业原因把类或方法标记为 sealed,以防第三方以违反授权协议的方式扩展该类。但一般情况下,在把类或成员标记为sealed 时要小心,因为这么做会严重限制它的使用方式。即使认为它不能对继承自一个类或重写类的某个 成员发挥作用,仍有可能在将来的某个时刻,有人会遇到我们没有预料到的情形,此时这么做就很 有用。.NET基类库大量使用了密封类,使希望从这些类中派生出自己的类的第三方开发人员无法访 问这些类。例如,string就是一个密封类。

把方法声明为sealed也可以实现类似的目的,但很少这么做。

7.修饰符: 

C#共有五种访问修饰符:public、private、protected、internal、protected internal。作用范围如下表:

访问修饰符说明
public公有访问。不受任何限制。
private私有访问。只限于本类成员访问,子类,实例都不能访问。
protected保护访问。只限于本类和子类访问,实例不能访问。
internal内部访问。只限于本项目内访问,其他不能访问。
protected internal内部保护访问。只限于本项目或是子类访问,其他不能访问

C#成员类型的可修饰及默认修饰符如下表:

成员类型默认修饰符可被修饰符
enumpublicnone
classprivatepublic、protected、internal、private、
protected internal
interfacepublicnone
structprivatepublic、internal、private

修饰符可以应用于类型的成员,而且有不同的用途:

修饰符应用于说明
new函数成员成员用相同的签名隐藏继承的成员
static所有成员成员不作用于类的具体实例
virtual仅函数成员成员可以由派生类重写
abstract仅函数成员虚拟成员定义了成员的签名,但没有提供实例代码
override仅函数成员成员重写了继承的虚拟或抽象成员
sealed类,方法和属性对于类,不能继承自密封类。对于属性和方法,成员重写已继承的虚拟成员,但任何派生类中的任何成员都不能重写该成员。该修饰符必须与override一起使用
extern仅静态[DllImport]方法成员在外部用另一种语言实现
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消