抽象类学习以及接口初识

抽象类部分

先了解一下概念
什么是抽象类?什么是接口?


1. 接口和抽象类都是“软件工程产物”
2. 具体类-》抽象类-》接口:越来越抽象,内部实现的东西越来越少
3. 抽象类是未完全实现逻辑的类(可以有字段和非public成员,他们代表了“具体逻辑”)
4. 抽象类为复用而生:专门作为类的基础来使用,也具有解耦功能
5. 封装确定的,开放不确定的,推迟到合适的子类中去实现
6. 接口是完全未实现逻辑的“类”(“纯虚类”;只有函数成员;成员全部public)
7. 接口为解耦而生:高内聚,低耦合,方便单元测试
8. 接口是一个“协约”,早已为工业生产所熟知(有分工必有协作,有协作必有协约)
9. 它们都不能实例化,只能用来声明变量,引用具体类(concrete class)

再了解一下开闭原则


        1. 如果不是为了修BUG或者添加新的功能的话,闲着没事别乱修改类的代码,特别是这个类中函数成员的代码
        2. 我们应该封装那些不变的,稳定的,固定的和确定的成员;而那些不确定的,有可能改变的成员应该声明为抽象成员并且留给子类去实现

 

具体一点,这个东西就是抽象类:
 

 abstract class Vehicle
    {
        public void Stop()
        {
            Console.WriteLine("Stopped!");
        }

        abstract public void Run();

    }

        1.  类前要用abstract修饰
        2. 抽象类表示函数成员没有被完全实现的类,即这个类里可以有若干个函数成员,但是至少有一个是未实现的
        3. 没有被实现的这个成员用abstract修饰,但是不能用private修饰,因为最后实现这个抽象类的一定是子类,但是子类没法访问private
        4. 成员一旦被abstract修饰,是不能有任何逻辑实现的
        5. 因为抽象类里有未被实现的抽象成员,没有具体行为,所以没法实例化
        6. 不能实例化,抽象类的作用就只剩两个了,一是作为基类让别人继承,在子类实现这些没有实现的成员;二是去声明变量,用这种基类类型的变量去引用自己的子类实例,这个子类已经实现了抽象类中一些未实现的方法,这是可以的

来看一个例子,注意请按照注释顺序查看:

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _12.抽象类
{

internal class Program
    {
        static void Main(string[] args)
        {
            //1.4 但是这样会出现一个新问题 往下看1.41
            //Vehicle vehicle = new Car();
            //vehicle.   点不出来Run方法,只有Stop,因为没在基类定义Run

            //1.6 此时vehicle.候选词既能看到Run也能看到Stop 往下看1.7
            //vehicle.Run();

            //2. 抽象类唯一能做的事就是给别的类当基类,以基类类型声明变量 ,并且引用那些已经完全实现了他那些抽象成员的子类实例来玩多态
            Vehicle v = new RaceCar();
            v.Run();
            Console.ReadLine();

        }
    }


    //1.3 所以我创建一个基类,并且把Car和Truck的Stop删除 往上看1.4
    abstract class Vehicle//1.72 类也要变成抽象类,此时也符合开闭原则 往下看1.73
    {
        public void Stop()
        {
            Console.WriteLine("Stopped!");
        }

        public void Fill()
        {
            Console.WriteLine("Pay and fill...");
        }

        //1.41 来看一个解决方法,但是这个方法违反开闭原则 往下看1.5
        //public void Run(string type)
        //{
        //    if (type=="car")
        //    {
        //        Console.WriteLine("Car is running......");
        //    }
        //    else if (type == "truck")
        //    {
        //        Console.WriteLine("Truck is running......");
        //    }
        //}
        //我们可以这样解决当前的问题,但是如果我又买了一辆车呢,就会频繁更改这个类

        //1.5 使用虚方法的重写解决这个问题 往下看1.51
        public abstract void Run();//1.71 此时就可以改成抽象成员 往上看1.72
        //{
        //    Console.WriteLine("Vehicle is running...");//1.7 此时发现这行根本没人用 往上看1.71
        //}



    }



    //1.0 先创建一个小汽车,车辆有启动和停止两种方法,往下看1.1
    class Car : Vehicle
    {
        //1.51 这里要加override 往下看1.52
        public override void Run()
        {
            Console.WriteLine("Car is running......");
        }

        //public void Stop()
        //{
        //    Console.WriteLine("Stopped!");
        //}

    }

    //1.1 我又买了个卡车,卡车也有上面两个方法,所以我复制下来改个名字 往下看1.2
    //1.2 此时他们有相同的停止方法Stop,而且我也违反了不能Copy/Pasted的原则 往上看1.3
    class Truck : Vehicle
    {
        //1.52 这里同上 往上看1.6
        public override void Run()
        {
            Console.WriteLine("Truck is running......");
        }

        //public void Stop()
        //{
        //    Console.WriteLine("Stopped!");
        //}
    }

    //1.73 比如我又买了一辆赛车,我在下面添加一辆赛车但是不需要在Vehicle增加删除成员 往下看1.74
    class RaceCar : Vehicle//1.74 此时报警问你是不是要实现抽象类,alt+enter选择“实现抽象类” 往下看1.75
    {
        public override void Run()//1.75 然后会自动生成这个方法 往上看2
        {
            //throw new NotImplementedException();这行不要
            Console.WriteLine("RaceCar is runnning...");
        }
    }
}

 抽象类和接口的关系

         看完上面的例子我们知道抽象类是具有一个或多个抽象成员的类,已知最少需要一个抽象成员,那最多能有几个呢?抽象类中可否全都是抽象成员?答案是可以!

当类中所有成员都是抽象成员的时候


请同样按照注释顺序阅读

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _12.抽象类
{

internal class Program
    {
        static void Main(string[] args)
        {

            Vehicle v = new RaceCar();
            v.Run();
            Console.ReadLine();

        }
    }

    //1. 创建一个全抽象的类,纯虚类  往下看1.1
    //2. 在C#里面,这种纯抽象类,纯虚类实际上就是接口 往下看2.1
    //abstract class  VehicleBase
    //{
    //    abstract public void Stop();
    //    abstract public void Fill();
    //    abstract public void Run();
    //}

    //2.1 来看看接口怎么定义,先把修饰改成interface 往下看2.2
    interface IVehicle  //VehicleBase 3. 接口的命名一般是I开头,这里将VehicleBase 替换为IVehicle 往下看3.1
    {
        //2.2 接口默认为纯虚公共方法,所以abstract public都可以删除 往下看2.3
        void Stop();
        void Fill();
        void Run();
    }

    //1.1 把Stop和Fill扔给了vehicle 往下看1.2
    abstract class Vehicle : IVehicle //VehicleBase 3.1 同上
    {
        //2.3 override要去掉,此时就变成了我们的类去实现接口 往下看2.4
        public void Stop()
        {
            Console.WriteLine("Stopped!");
        }

        public void Fill()
        {
            Console.WriteLine("Pay and fill...");
        }
        // public abstract void Run();//这行可以不要,已经在上面定义了


        //2.4 此时继承类Vehicle:VehicleBase会报警,因为没有实现接口成员Run,只实现了另外两个,此时可以保留一个abstract下推给其他派生类 往上看3
        abstract public void Run();

    }


    //1.2 Run继续往下扔给Car他们 往上看2
    class Car : Vehicle
    {

        public override void Run()
        {
            Console.WriteLine("Car is running......");
        }
    }

    class Truck : Vehicle
    {

        public override void Run()
        {
            Console.WriteLine("Truck is running......");
        }


    }


    class RaceCar : Vehicle
    {
        public override void Run()
        {
            Console.WriteLine("RaceCar is runnning...");
        }
    }
}

        通过上面的例子我们知道在C#里面,纯抽象类或纯虚类实际上就是接口
        在本例的继承体系当中,我们把纯虚类替换成了接口,接口下推一级我们用一个抽象类Vehicle做了一个不完全的实现,然后用这个不完全实现的抽象类作为基类,再派生出其他的具体类Car,Truck和RaceCar

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值