zoukankan      html  css  js  c++  java
  • 继承

     

                Person zsPerson = new Chinese();

     

    继承

    前言:在本篇的博客中,我们会讲解继承的类型、实现继承、访问修饰符、接口。继承时我们本次的主题,我们将讨论c#和.Net Framwork如何处理继承。

    一:继承的类型:

    1.1:实现继承和接口继承:

    在面向对象的编程中,有两种截然不同的继承类型:实现继承和接口继承。

    实现继承:表示一个类型派生于基类型。它拥有该基类型的所有成员字段和函数。在实现继承中,派生类型采用基类型中每个函数的实现代码,除非在派生类型的定义中指定重写某个函数的实现代码。在需要给现有的类型添加功能。或许多相关的类型共享一组重要的公共功能时,这种类型的继承非常有用。

    接口继承:表示一个类型只继承了函数的签名。没有继承任何实现代码。在需要指定该类型具有某些可用的特性时,最好使用这种类型的继承。 

    1.2:多重继承:

    c#不支持多重实现继承。而c#又允许类型派生自多个接口——多重接口继承。这说明,c#类可以派生自另一个类和任意多个接口。更准确的说:因为System.Object是一个公共的基类,所以每个c#(除了Object类之外)都有一个基类,还可以有任意多个基接口。 

    1.3:结构和类:

    使用结构的一个限制是结构不支持继承,但每个结构都自动的派生于System.ValueType。不能编码实现类型层次的结构。但接口可以实现接口。换言之:结构并不支持继承,但是支持接口继承。 

    结构总是派生自System.ValueType,他们还可以派生自任意多个接口。

    类总是派生于System.Object或用户选择的另一个类,他们还可以派生自任意多个接口。

    二:实现继承:

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

     

        public class Chinese : Person
        {
            
        }

     


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

        public class Chinese : Person, IInterface1, IInterface2
        {
    
        }

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

        public class Chinese : object
        {
    
        }
        public class Chinese
        {
    
        }

    如果要引用Object类,就可以使用object关键字,智能编译器会识别它,因此便于编辑代码。

    2.1:虚方法:把一个基类函数声明为virtual,就可以在任何派生类中重写该函数:

            public virtual string VirtualMathod()
            {
                return "The method is virtual and defined in MyBaseClass";
            }


    c#中虚函数的概念与标准OOP的概念相同:可以在派生类中重写虚函数。在调用方法时,会调用该类对象的合适方法。在c#中,函数在默认的情况下不是虚拟的,但(除了构造函数以外)可以显示的声明virtual。除非显示指定,否则函数就不是虚拟的。因此在c#要求在派生类的函数重写另一个函数时,要使用override关键字显示的声明:

            public override string VirtualMathod()
            {
                return "This method is an override defined in MyDerivedClass";
            }


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

    三:抽象类和抽象函数:

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

     

            public abstract string VirtualMathod();//abstract method

     

    四:密封类和密封方法:

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

    要在方法或者属性上使用sealed关键字,必须先从基类上把它声明为要重写的方法或者属性。如果基类上不希望有重写的方法或者属性,就不要把它声明为virtual。

    五:派生类的构造函数:

    在创建派生了类的实例的时候,实际上会有多个构造函数起作用。要实例化的类的构造函数本身不能初始化类,还必须调用基类中的构造函数。

        public abstract class Person
        {
    
            private string _name;
            public string Name
            {
                get { return _name; }
                set { _name = value; }
            }
    
        }
        public class Chinese
        {
            private int _age;
        }

    客户端的调用:

                Person zsPerson = new Chinese();

    显然,成员字段name和age都必须在实例zsPerson时进行初始化。如果没有提供自己的构造函数,而是仅依赖于默认的构造函数,那么name会初始化为null引用。age初始化为0.这就有关于我们构造函数的执行的顺序了。

    我们首先来理解一下构造函数的执行的顺序:假定默认的构造函数一直在使用:编译器首先找到视图实例化的类的构造函数,在本类中是Chinese,这个默认的Chinese构造函数为其直接基类Person运行默认的构造函数。然后Person构造函数为其直接基类System.Object运行默认的构造函数。System.Object没有任何的基类,所有他的构造函数执行。并把控制权返给Perso构造函数,现在执行的是Person构造函数,把name初始化为null,在把控制权返回给Chinese构造函数,接着执行这个构造函数,把age初始化为0,并且退出。此时,Chinese实例就已经成功了构造和初始化了。

    我们注意构造函数的执行的顺序:最先调用的是总是基类的构造函数。先调用System.Object,在按照层次的结构由上到下进行。直到到达编译器要实例化的类为止。每个构造函数都初始化自己类中的字段。

    六:在层次结构中添加带参数的构造函数:

    首先是带一个参数的Person构造函数,它仅在提供其姓名时才实例化:

            private string _name;
            public string Name
            {
                get { return _name; }
                set { _name = value; }
            }
    
            protected Person(string name)
            {
                this.Name = name;
            }


    在编译器试图为派生类创建默认的构造函数的时候,会产生一个编译错误,因为编译器为Chinese生成的默认构造函数视图调用一个无参数的Person构造函数。但是Chinese没有这样的构造函数。因此,需要为派生类提供一个构造函数,来避免这个错误(使用base调用父类的构造函数)。

        public class Chinese : Person
        {
            public Chinese(string name) : base(name)
            {
            }
        }

    所以当我们new对象的时候会执行我们的构造函数,进行初始化赋值的操作。

                Person zsPerson = new Chinese("张三");


    我们接下里做一个练习,首相我们声明一个Person,里面有我们的数据成员,包括名字、年龄、性别。我们自己声明的构造函数进行对字段的初始化赋值。

        public class Person
        {
            private string _name;
            public string Name
            {
                get { return _name; }
                set { _name = value; }
            }
            private int _age;
            public int Age
            {
                get { return _age; }
                set { _age = value; }
            }
            private char _gender;
            public char Gender
            {
                get { return _gender; }
                set { _gender = value; }
            }
    
    
            public Person(string name, int age, char gender)
            {
                this.Name = name;
                this.Age = age;
                this.Gender = gender;
            }
        }

    子类的声明,继承父类的构造函数(因为编辑器默认调用时父类的无参数的构造函数,但是它没有了),所以需要显示的调用父类有参数的构造函数。子类也可以有自己的数据成员,接下来我们声明一个程序员类继承父类Person:

        //程序员类
        public class Programmer : Person
        {
            //工作时间
            private int _workYear;
    
            public int WorkYear
            {
                get { return _workYear; }
                set { _workYear = value; }
            }
    
            public void ProgrammerSayHello()
            {
                Console.WriteLine("我叫{0},我是一名程序猿,我是{1}生,我今年{2}岁了,我的工作年限是{3}年",this.Name,this.Gender,this.Age,this.WorkYear);
            }
    
    
            public Programmer(string name, int age, char gender, int workYear)
                : base(name, age, gender)
            {
                this.WorkYear = workYear;
            }
        }

    客户段程序的调用:

                Programmer pro = new Programmer("程序猿", 23, '', 3);
                pro.ProgrammerSayHello();
    
                Console.ReadKey();

     

  • 相关阅读:
    【读书笔记】MSDN 上关于加密解密的一个例子
    【读书笔记】创建泛型编程类 由链表而深入
    HDFS数据加密空间Encryption zone
    各种数据库的连接方法
    Java基础5
    Java基础4
    Java基础2
    博客优化、收录、RSS技巧
    Java基础3
    Java基础8
  • 原文地址:https://www.cnblogs.com/MoRanQianXiao/p/7805502.html
Copyright © 2011-2022 走看看