C#基础知识点总结(三)- 继承

1. 继承

类可以单重继承(B:A),不允许类多重继承,但是接口可以多重继承

2. 继承的类型

1. 单重继承:一个类可以派生自一个基类

2. 多重继承:接口多重继承,C#不支持类的多重继承

类可以派生自另一个类和任意多个接口,类总是必须放在接口前面

3. 多层继承:继承有更多层次结构。类B(中间基类)继承自A,C又派生自B

4. 接口继承:定义了接口的继承,允许多重继承

结构不支持继承,但结构可以实现接口,支持接口继承

3. 实现继承

1. 子类不能继承父类的构造函数

2. 子类会首先去默认执行父类的无参构造函数,然后再执行自己的构造函数。子类始终要使用父类的一个构造函数在子类内部创建一个父类对象,为了调用父类成员。子类默认调用父类的无参构造函数,所以在显示编写一个有参构造函数时导致父类没有了无参构造函数,从而编译出错

3. 构造函数和析构函数不能被继承,除此外其他成员都可以被继承

4. 基类中成员的访问方式只能决定派生类是否能访问它们(比如基类是private的字段,派生类就不能访问这个字段)

    //PhoneCustomer类继承了PhoneClass类和IPhoneInfo, Iinterface两个接口
    //类和接口都用于派生,则类必须放在接口前面
    public class PhoneCustomer: PhoneClass, IPhoneInfo, Iinterface
    {
    }
    
    //结构只能用于接口继承
public struct MyData: IInterface1, IInterface2 { }

3.1 虚方法(virtual)

1. 把一个基类方法声明为virtual,就可以在任何派生类中重写该方法

2. 属性也可以声明为virtual,对于虚属性或重写属性,语法与非虚属性相同

    public class Shpae
    {
        //Draw为一个虚方法
        public virtual void Draw()
        {
            Console.WriteLine($"Shape with{Position} and {Size}");
        }
    }

    //属性也可以声明为virtual,对于虚属性或重写属性,与普通属性相同
    public virtual Size size { get; set; }
    private Size _size;
    public virtual Size Size
    {
        get { return _size; }
        set { _size = value; }
    }

3. 派生类的函数重写另一个函数时,要使用override关键字显式声明

    //Rectangle类重写Shape类中的Draw()方法,要显示的声明override
    public class Rectangle: Shape
    {
        public override void Draw()
        {
            Console.WriteLine($"Rectangle with{Position} and {Size}");
        }

    }
//重写ToString()方法

public override string ToString() => $"X: {X},Y: {Y}";

4. 重写基类方法时,所有参数类型和方法名和返回类型必须完全匹配

5.成员字段和静态函数都不能声明为virtual,只对类中的实例函数成员有意义

6. 子类继承虚拟类时,虚拟方法实现不现实都可以

7. 虚拟函数和正常函数无区别

8. 只有为虚方法时可以进行重写,不能对非虚方法进行重写

9. 当用virtual修饰后,不能再有static, abstract, override等修饰符

10. 虚方法也可以像普通方法一样,实例化包含虚方法的类后,用对象调用虚方法执行

3.2 多态性

使用多态性,可以动态的定义调用的方法,而不是在编译期间定义。多态的作用就是把不同的子类对象都当做父类来看。

1.父类类型指针可以指向子类类型对象

2. 实现多态几种方式,抽象类,虚方法,接口

3.3 隐藏方法(new)

签名(所有参数类型和方法名)在基类和派生类中都进行了声明,但方法没有声明为virtual和override,则派生类的方法就会隐藏基类方法(在派生类中用派生类定义的方法的,而不是基类的)

1.大多数情况下,应该重写方法,而不是隐藏方法

2. new修饰符主要用于处理版本冲突,不应该故意用于隐藏基类的成员

3. 子类和父类成员相同的时候,子类成员将父类的成员隐藏了,隐藏后子类将无法访问到父类的成员。如果子类有意隐藏父类那个相同的成员(不用父类的,要用子类的),则用new关键字显示说明

3.4 调用方法的基类版本(base)

从派生类中调用方法的基类版本

1.使用base关键字,已重写的基类方法也可以调用,可以调用基类的任何方法

    public class Shpae
    {
        //Move为一个虚方法
        public virtual void Move(Position pos)
        {          
        }
    }
    public class Rectangle : Shape
    {
        public override void Move(Position newpos)
        {
            Console.WriteLine($"{pos}");
            //Rectangle类继承了Shape类,并重写了Move方法,但是现在想在Rectangle类中调用Shpae中的Move方法
            base.Move(newpos);
        }

3.5 抽象类和抽象方法(abstract)

抽象类使用abstract修饰符,并且它只能是用作基类,不能被实例化

1. 抽象类不能实例化,当使用new对其实例化则编译错误。抽象方法不能直接实现,必须在非抽象的派生类中重写

2. 抽象类中的抽象方法不能在抽象类中有任何实现代码

3. 派生类继承抽象类时,必须实现所有的抽象成员

4. 抽象方法也是虚拟的(但是不能提供virtual,会报错)。不能使用private,static, virtual

5. 如果类包含抽象方法,则该类也是抽象的,必须声明为抽象的类。非抽象类不能包括抽象成员

6. 抽象类不能被密封

7. 抽象类可以被抽象类继承,结果仍是抽象类

8. 抽象方法不能在派生类中用base访问

9. 抽象类中可以存在非抽象方法(普通方法,虚方法等),抽象方法必须包含在抽象类

10. 派生类中在实现抽象方法用override关键字,如果子类没有实现抽象基类中的所有抽象方法,则子类必须定义成一个抽象类

11. 抽象方法被实现后,不能更改修饰符

12. 抽象类可以定义字段,属性,方法实现

    public abstract class Shpae
    {
        //抽象方法
        public abstract void Resize(int width, int height);
    }

    public class Ellipse: Shpae
    {
        //从抽象基类中派生类型时,需要实现所有的抽象成员
        public override void Resize(int width, int height)
        {
            Size.Width = width;
            Size.Height = height;
        }
    }

3.6 密封类和密封方法(sealed)

如果不应创建派生自某个自定义类的类,则该自定义类就应密封。给类添加sealed修饰符,就不允许创建该类的子类了,密封一个方法,表示不能重写该方法

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

2. 不应密封类型和成员

3. 密封类不能作为基类被继承,但它可以继承别的接口或类

4.密封类中不能声明受保护的成员或虚成员

5. sealed与abstract不能同时使用

6. 密封方法只能用于对基类的虚方法进行实现,并提供具体的实现,sealed总是与override一起使用

7. 使用密封类,满足如下条件:

类是静态类。

类包含带有安全敏感信息的继承的受保护成员。

类继承多个虚成员,并且密封每个成员的开发和测试开销明显大于密封整个类。

类是一个要求使用反射进行快速搜索的属性。密封属性可提高反射在检索属性时的性能

    sealed class PhoneCustomer
    {
        //
    }
    // 错误,Derive类不能继承PhoneCustomer,因为PhoneCustomer是密封的
    class Derive : PhoneCustomer
    {

    }

3.7 派生类的构造函数

1. 在派生类中,先执行基类的构造方法,在执行派生类的构造方法

2. 当基类中编写构造函数时,派生类没有指定调用哪个构造函数时,会寻找无参的构造函数,如果没有,则报错,另外无论调用派生类中哪个构造函数,都是去寻找无参的那个基类构造函数,而非参数匹配

3. 如果基类中没有无参构造函数,那么派生类中的构造函数必须全部指定调用的基类构造函数

4. 修饰符

508ad920de9d4270a0110ca3448bbdd3.png

 b7ac3a7787604d61a1ec0c6a31d395b1.png

5. 接口

1. 不允许提供接口中任何成员的实现方式,一般情况下,接口只包含方法,属性,索引器和事件的声明

2. 接口不能有任何实现代码,它是纯抽象的

3. 接口既不能有构造函数也不能有字段

4. 接口不允许包含运算符重载

5. 接口定义中不允许声明成员的修饰符,接口成员总是隐式为public,不能声明为virtual和static

6. 接口永远不能实例化,它只能包含其成员的签名,可以声明接口类型的变量

7. 接口通常以字母I开头

8. 接口可以继承接口

9. 继承接口的类,必须实现接口中所有的方法和属性

    public interface IBankAccount
    {
        void PayIn(decimal amount);
        bool Withdraw(decimal amount);
        decimal Banlance { get; }
    }

    //SaveAccount实现了IBankAccount所有的方法和属性
    public class SaveAccount : IBankAccount
    {
        private decimal _balance;
        public void PayIn(decimal amount) => _balance += amount;
        public bool Withdraw(decimal amount)
        {
            if(_balance > amount)
            {
                return true;
            }
            return false;
        }

        public decimal Banlance => _balance;
    }

6. is和as运算符

转载自:C#中as和is的用法详解_Code Flying的博客-CSDN博客_c# is

6.1 is

is检查对象是否与给定类型兼容。

6.2 as

as用于在兼容的引用类型之间执行转换。

7. 抽象类和接口的区别

转载自:c#:浅析接口(interface)与抽象类(abstract)的区别_Peter_Gao_的博客-CSDN博客_c# abstract和interface的区别

 8. 虚方法和抽象方法区别

 

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值