• C# 在线培训之零基础入门 06:面向对象入门


    在本课程的第一节“.NET 零基础入门 01:开篇及C#程序、解决方案的结构”中,我们说明为了要选择C#作为你成为程序员的第一门语言:

    • 首先,C#是一门非常优秀的面向对象编程的语言;

    凡是对编码感兴趣的同学一定听说过“面向对象编程”这个概念,C#就是为此诞生的,它天然是面向对象的。所以,既然“面向对象编程”是当前IT界的主流,我们选择C#就没有偏离主流方向。

    本节,我们就要讲讲什么是面向对象,以及面向对象开发中最重要,最应该掌握的概念。

    一:什么是面向对象?

    “面向对象”是当前软件开发的一个主流思想,它有三个主要特征:封装、继承、多态。很多软件开发的教程使用了阿猫阿狗的例子来讲面向对象,其实说明 了“面向对象”在某种意义上是很好理解的概念。我们已经在之前的例子中讲过了“类”这个概念,现在我们就来讲讲抽象类,接口。当然,我们不打算用阿猫阿狗 的例子(阿猫阿狗都是动物,动物都有吃喝拉撒的习惯……,但是阿猫有阿猫的喵喵叫,阿狗有阿狗的汪汪叫……),我们在这里举一个实际的例子,只不过,需要 大家活动思维,主动将概念联系到阿猫阿狗中去。

    这个实际的例子是.Net Framework基础类库(以后简称:FCL)中的三个类Stream、FileStream 、MemoryStream ,如下:

    public abstract class Stream
    {
        public abstract void Write(byte[] buffer, int offset, int count);
    }

    public class FileStream : Stream
    {
        public override void Write(byte[] array, int offset, int count)
        {
           
        }
    }

    public class MemoryStream : Stream
    {
        public override void Write(byte[] buffer, int offset, int count)
        {
           
        }
    }

    这三个是负责byte读写的类,我只拿出了其中一个方法Write。

    (TIP:什么是byte?,程序中的数据会以byte的形式存在,我们只要知道,读写内容,如字符串、整数,到内存中,到文件中,实际读写的就是byte就行了)

    1:什么是封装?

    一句话概括:像上面这样,讲代码分类到不同的类中,实际就是封装。

    2:什么是继承?

    像上面这样,有父类Stream,有两个子类,就叫做继承。如果要讲继承,我们就需要讲解下访问限制符了。

    2.1什么是类型的访问限制符?

    在C#中,可以为类型,如Stream类,或者类型的成员,如Write方法,添加访问限制符。在C#的世界中,我们将类型和类型的成员所在的可见范围分为如下几类(注意,只讲解最常用的):

    a)在整个应用程序内可见

          使用public修饰。

    b)在当前项目内可见

          使用internal修饰。

    c)在当前类型内部可见

          使用private修饰。该修饰符主要修饰类型的成员。

    d)仅在子类中可见

          使用protected修饰。该修饰符主要修饰类型的成员。

    需要强调的是,类型的默认访问限制符是internal,如果我们不加任何修饰符,.NET编译器就会默认此类型为internal。类型的成员的访问限制符是private。

    好了,现在不妨停下来,自己创建一些类型和类型成员,感悟下对访问限制符的理解吧。

    2.2什么是抽象类?

    如何创建一个类的对象?使用new,如下:

    Stream stream = new Stream();

    但是这段代码你一定执行不了,因为Stream是抽象类,它被abstract修饰着。既然抽象类new不了?那它有什么意义呢?

      a)首先,它可以定义一些抽象的方法abstract method,抽象方法表达了这样一个意思:嗨,子类,你必须实现我这个方法;

      b)其次,抽象类可以有一些常规的非abstract的方法,也就是说,我们可以把一些子类中相同的逻辑,放到这个抽象方法中,这样就不需要相同的代码在不同的子类中都去写一遍了。

    2.3什么是接口?

    如果一个抽象类,只有抽象方法,而没有常规方法,那么我们可以考虑将它抽象为接口(interface)。一个简单的接口的例子如下:

    interface IDispose
    {
        void Dispose();
    }

    那么,什么时候用接口,什么时候用抽象类呢?这个问题问的很好,但是,别着急,作为初学者,我们是很难把握这个标准的,对于现在的我们来说,我们只要直接.NET的世界中,存在类、抽象类、接口就可以了,尝试一口气全部弄明白,往往属于揠苗助长。

    3:什么是多态?

    一句话概括之:让子类有自己的行为,就是多态。好吧,其实,多态可不是一句话能概括的,我们来具体看看多态的具体含义及实现手段。

    二:多态的具体含义及实现手段

    备注:以下内容摘自我所撰写的书《高质量代码编写:改善C#的157个建议》。注意,要完全掌握多态的含义及实现手段,必须通过自己编写代码细细品味才行,请按照本文下面的例子来体会。

    override和new使我们的类型体系因为继承而呈现出多态性。多态是一门语言是否是“面向对象语言”的三个重要特性之一。多态要求子类具有与基类方法同名的方法,而override和new的作用就是:

    如果子类中的方法前面带有new关键字,则该方法被定义为独立于基类的方法。

    如果子类中的方法前面带有override关键字,则子类的对象将调用该方法,而不是调用基类方法。

    这两个定义看上去有些抽象,要深刻理解它们之间的区别,我们不妨来看一个继承体系:

    public class Shape

    {

    public virtual void MethodVirtual()

    {

    Console.WriteLine("base MethodVirtual call");

    }

    public void Method()

    {

    Console.WriteLine("base Method call");

    }

    }

    class Circle : Shape

    {

    public override void MethodVirtual()

    {

    Console.WriteLine("circle override MethodVirtual");

    }

    }

    class Rectangle : Shape

    {

    }

    class Triangle : Shape

    {

    public new void MethodVirtual()

    {

    Console.WriteLine("triangle new MethodVirtual");

    }

    public new void Method()

    {

    Console.WriteLine("triangle new Method");

    }

    }

    class Diamond : Shape

    {

    public void MethodVirtual()

    {

    Console.WriteLine("Diamond default MethodVirtual");

    }

    public void Method()

    {

    Console.WriteLine("Diamond default Method");

    }

    }

    查看上面的继承体系,类型Shape是所有子类的基类。

    Circle类override了父类的MethodVirtual,所以即使子类转型为了Shape,调用的还是子类的方法:

    Shape s = new Circle()

    s.MethodVirtual();

    s.Method();

    输出:

    circle override MethodVirtual

    base Method call

    当然,Circle的第二种用法很好理解。使用子类本身的类型,调用的则全部是子类的方法,如下所示:

    Circle circle = new Circle();

    circle.MethodVirtual();

    circle.Method();

    输出也是:

    circle override MethodVirtual

    base Method call

    类型Rectangle没有对基类做任何处理,所以无论子类是否转型为Shape,调用的都是基类Shape的方法。

    类型Triangle将基类Shape的virtual方法和非virtual方法都new了一遍。所以第一种用法为:

    Shape s = new Triangle()

    s.MethodVirtual();

    s.Method();

    输出:

    triangle new MethodVirtual

    triangle new Method

    类型Diamond,包含了两个和基类一模一样的方法,并且没有额外的修饰符。这在编辑器中会提出警示。但是如果选择忽略这些警示,程序一样还是可以运行。它的第一种用法为:

    Shape s = new Diamond()

    s.MethodVirtual();

    s.Method();

    编译器会默认new的效果,所以输出和显式new修饰的一样。

    输出:

    base MethodVirtual call

    base Method call

    在Diamond的第二种用法中,全部调用的是子类的方法,如下所示:

    Diamond diamond = new Diamond();

    diamond.MethodVirtual();

    diamond.Method();

    输出:

    Diamond default MethodVirtual

    Diamond default Method

    在本建议的最后,我们总结一下以上所有的用法,并给出一个综合示例,读者可以仔细体会每种用法所带来的输出变化:

    static void Main(string[] args)

    {

    TestShape();

    TestDerive();

    TestDerive2();

    }

    private static void TestShape()

    {

    Console.WriteLine("TestShape\tStart");

    List<Shape> shapes = new List<Shape>();

    shapes.Add(new Circle());

    shapes.Add(new Rectangle());

    shapes.Add(new Triangle());

    shapes.Add(new Diamond());

    foreach (Shape s in shapes)

    {

    s.MethodVirtual();

    s.Method();

    }

    Console.WriteLine("TestShape\tEnd\n");

    }

    private static void TestDerive()

    {

    Console.WriteLine("TestDerive\tStart");

    Circle circle = new Circle();

    Rectangle rectangle = new Rectangle();

    Triangle triangel = new Triangle();

    Diamond diamond = new Diamond();

    circle.MethodVirtual();

    circle.Method();

    rectangle.MethodVirtual();

    rectangle.Method();

    triangel.MethodVirtual();

    triangel.Method();

    diamond.MethodVirtual();

    diamond.Method();

    Console.WriteLine("TestShape\tEnd\n");

    }

    private static void TestDerive2()

    {

    Console.WriteLine("TestDerive2\tStart");

    Circle circle = new Circle();

    PrintShape(circle);

    Rectangle rectangle = new Rectangle();

    PrintShape(rectangle);

    Triangle triangel = new Triangle();

    PrintShape(triangel);

    Diamond diamond = new Diamond();

    PrintShape(diamond);

    Console.WriteLine("TestDerive2\tEnd\n");

    }

    static void PrintShape(Shape sharpe)

    {

    sharpe.MethodVirtual();

    sharpe.Method();

    }

    输出:

    TestShape Start

    circle override MethodVirtual

    base Method call

    base MethodVirtual call

    base Method call

    base MethodVirtual call

    base Method call

    base MethodVirtual call

    base Method call

    TestShape End

    TestDerive Start

    circle override MethodVirtual

    base Method call

    base MethodVirtual call

    base Method call

    triangle new MethodVirtual

    triangle new Method

    Diamond default MethodVirtual

    Diamond default Method

    TestShape End

    TestDerive2 Start

    circle override MethodVirtual

    base Method call

    base MethodVirtual call

    base Method call

    base MethodVirtual call

    base Method call

    base MethodVirtual call

    base Method call

    TestDerive2 End

    TMJ .NET在线培训”本节知识点:

    1:面向对象;

    2:抽象类、接口;

    3:抽象方法;

    4:访问限制符;

    5:override,new;

    6:多态;

    --------------------------------------

    欢迎您,进入 我系程序猿 的cnBlog博客。

    你不能改变你的过去,但你可以让你的未来变得更美好。一旦时间浪费了,生命就浪费了。

    You cannot improve your past, but you can improve your future. Once time is wasted, life is wasted.

    --------------------------------------

    分享到QQ空间  

  • 相关阅读:
    CentOS 7中cAdvisor的安装过程
    怎么查看centos版本
    使docker命令不用加sudo的方法
    Linux进程相关函数system,fork,exec函数族的区别
    git pre-commit hook failed 解决办法
    lint-staged 教程
    用webpack将多个scss文件打包成一个css文件
    js连按键盘事件
    vscode快捷键
    vim 操作命令大全
  • 原文地址:https://www.cnblogs.com/jqmtony/p/2910839.html
Copyright © 2020-2023  润新知