• C#面向对象的三大特性:封装、继承、多态。
  • 这是一种特性,更是官方给我们的学习语法,但是我们根据过去的经验来思考一下,
    1. 到底什么是面向对象?
    2. 面向对象在我们实际开发中到底起着什么作用?
    3. 我们什么时候要用面向对象,他真的为我们解决了什么?
    4. 假如不用面向对象行不行?
  • 下面我们来逐步分析一下:
  • 到底什么是面向对象?
  1. 提到面向对象时,我们不得不提一下面向过程,面向过程典型的代表就是:C语言开发。
  2. C语言是面向过程,执行顺序由前到后,逻辑表达比较清晰,相对而言,这种逻辑开发更加符合我们日常的思维方式,因为我们平时做事也是按顺序思考;正因为符合我们的日常思维方式,优点:更容易理解,减少了反复实例化的过程,内存认知能力更快,因为不管什么语言都要最终编译成机器码(也就是二进制0 1)来处理,既然我没有那么多对象,所以本身资源分配会高;缺点:后期维护性和复用性降低,为什么呢,因为我就是按顺序执行的,你怎么让我复用,你要维护时,是不是也得从前向后去找我。
  3. 举个例子:比如我们在编写C时,我的入口主方法:
  4. #include "stdafx.h"
    #include <stdio.h>
    #include <Windows.h>
    #define _CRT_SECURE_NO_WARNINGS int main()
    {
    char chr[];
    scanf_s("%s", &chr,sizeof(chr));
    myWrite(chr);
    system("pause");
    return ;
    }
    int myWrite(char *chr)
    {
    printf("哈哈:%s\n", chr);
    return ;
    }

    以上代码是执行不下去的,为什么,我既然是面向过程,你怎么能先调用后定义呢?显示不行,必须把myWrite()函数移到main()函数之上才行。或者

  5. 或者,你提前把我定义一下也行啊;如:
  6. int myWrite(char *chr);
    int main()
    {
    char chr[];
    scanf_s("%s", &chr,sizeof(chr));
    myWrite(chr);
    system("pause");
    return ;
    }
    int myWrite(char *chr)
    {
    printf("哈哈:%s\n", chr);
    return ;
    }

    这就是典型的面型过程。

  7. 面向对象的典型代表就是:C#、C++、Java等,以C#为例说明。
    • 我们先来说面向对象的三大特性:封装性、继承性、多态性。    
    • 什么是封装:封装就是把所有处理细节统一起来,只预留调用的接口和参数约定,来完成功能流程的处理,这样被我封装的部分将根据不同的要求进行访问控制,最终完成需求。
    • 封装我们都是以类为模板进行,类里面可以有多个实现方法,每个封装的类或方法又有不同的访问级别。如下:
    • class Program
      {
      static void Main(string[] args)
      {
      Student student = new Student();
      int stuNum = student.stuCount();
      Console.WriteLine(stuNum); Console.ReadKey();
      }
      }
      public class Student
      {
      public int stuCount()
      {
      return ;
      }
      }

      简单封装了一个类Student,关于学生信息的处理都集中到这个类里,然后实例化调用相关方法。

      • 在实际使用过程中,封装主要用于两大方面:
        1. 功能性封装,比如一个事务下的,模块下的,同类型下的逻辑处理,这时候我们要考虑封装。
        2. 实体类处理,我们在处理数据实体类时,经常进行封装,一般是一个表或+扩展字段后,进行逻辑字段和属性的封装。
    • 什么是继承:在处理类对象时,对于有重复性思考的对象,我们要用继承来处理。并且类是单继承模式,类不能同时既继承A,又继承B;
        • 继承的作用主要在封装基础上,进行的扩展,为什么这样说呢?既然是继承,其实就是共享性东西的再封装,举例如下:
        • class Program
          {
          static void Main(string[] args)
          {
          Car c = new Car("汽车类型"); Geely geely = new Geely();
          geely.CarName = "吉利";
          geely.Nationality = "China";
          geely.SayCar(geely.CarName, geely.Nationality); Console.ReadKey();
          }
          }
          class Car
          {
          string _carType = "";
          public Car()
          { }
          public Car(string carType) : this()
          {
          _carType = carType;
          } private string carName;
          public string CarName
          {
          get { return carName; }
          set { carName = value; }
          }
          }
          class Geely : Car
          {
          string _carName = "";
          public Geely()
          { }
          public Geely(string carName) : this()
          {
          _carName = carName;
          }
          private string nationality;
          public string Nationality
          {
          get { return nationality; }
          set { nationality = value; }
          }
          public void SayCar(string name, string nation)
          {
          Console.WriteLine("我叫" + _carName + "," + "我属于" + nation);
          Console.WriteLine("我叫" + name + "," + "我属于" + nation);
          }
          }

          我定义了一个Car类,是基类,又定义了一个Geely类,是子类,并且分别对构造函数进行了参数传递。运行结果如下:

        1. 第一个结果显示_carName为空,可见构造函数本身是不被继承的。

        2. 提取出了公共对象name,因为不管那个国家的车,都会有名字,单独对共享的东西进行了封装,所以说继承也是更进一步的封装。
      • 假如我就不想被继承怎么办?有办法我们提供了一种类叫密封类:格式如下: 
      • sealed class DaZhong
        { }
        class shanghaiDZ : DaZhong
        {
        //这个时候继承是报错的,提示无法从密封类中继承,那么DaZhong本身就不能被任何子类继承了。
        }
    • 什么是多态?我相信,封装、继承作为面向对象的前两大特性,不难,很多人一提多态就晕,很正常,有时候你工作后是不是发现,不知道你学的多态到底在干什么,好像是不是项目中就不知道用没用到多态,那里用的,带着这个问题,我们来思考一下。

      • 多态多态,字面意思来说就是多种态度,既然是编代码嘛,那就是说在不同的语境下他会呈现不同的态度。
      • 为什么我们觉得封装和继承很好理解,但是一说多态都不知道是什么,如果这样感觉,那你就感觉对了,因为多态本身就什么也不是,多态是一个很抽象的概念,是一个来形容面向对象特性的一个关键词,而其实多态就好比一个大平台大容器,他带有的很多特点特色又组成了这个多态容器,而多态容器+继承+封装:又反应出了面向对象的特色。
      • 下面我们通过理论+实践例子,来具体分析一下多态这个大容器的一些特性;如下:
      1. 重写基类方法
        1. 例子如下:

          • //父类Car
            class Car
            {
            public virtual void Fun()
            {
            Console.WriteLine("我是车类型");
            }
            }
            //继承Car
            class DZCar:Car
            {
            public override void Fun()
            {
            Console.WriteLine("我是大众");
            }
            public void Test()
            {
            Fun();
            base.Fun();
            }
            }
            static void Main(string[] args)
            {
            Car car = new Car();
            car.Fun(); //父类fun
            Car dzCar = new DZCar();
            dzCar.Fun(); //子类fun
            DZCar car2 = new DZCar();
            car2.Fun();//子类fun
            car2.Test();//子类fun,父类fun Console.ReadKey();
            }

            运行结果如下如右侧注释,说明:重写override后,结果就都是重写后的子类方法,其中base含义代表指向父类。

        2、隐藏基类方法

      1. 例子如下:

                • class MyBase
                  {
                  public virtual void Fun()
                  {
                  Console.WriteLine("我是基类虚方法");
                  }
                  }
                  class MyChild:MyBase
                  {
                  public new virtual void Fun()
                  {
                  Console.WriteLine("我是子类虚方法");
                  }
                  } MyChild mc = new MyChild();
                  mc.Fun(); //子类
                  MyBase mb = mc;
                  mb.Fun(); //父类
                  MyBase mb2 = new MyBase();
                  mb2.Fun(); //父类

                  运行结果如右侧注释,new作为隐藏基类方法那么结果就是根据定义的类型来决定输出父类还是子类,赋值给MyBase,所以就输出父类方法。

                  面向对象版计算器源码下载:

                      链接:https://pan.baidu.com/s/1bkBkoWp8RAUlii718wh_5Q
                      提取码:bbwg

                • 那么另一个问题来了,这只是目前面向对象的一个方面而已,利用这个例子我们分析一下,我们在实际开发中怎么去锻炼这个面向对象的思维呢,我又是怎么知道要做面向对象的设计呢?

                • 我个人总结以下几点:

                  1. 碰见问题时,先不要刻意的说就必须面向对象,就必须把这个瓜强扭起来,我们先按面向过程的思维逻辑顺下来。因为面向过程的思维逻辑相对比较符合我们的日常思维逻辑。

                  2. 当你面向过程的逻辑顺下来后,那么我们就要分析,那些是重用的,那些是独立的,那些可以封装、继承,那些又是抽象出来需要以后扩展的,这时候就需要分门别类,把一些特性和理论给利用起来,逐步实现面向对象的思维模式。

                  3. 当你初步有了面向对象思维后,那么更重要的一点就是项目实践,学了不用,等于0基础,过段时间肯定忘。

                  4. 要坚持一个原则,面向对象本没有固定对象,也没有哪一种面向后就绝对好,要形成自己的思路来面向对象,反正地基大家都是用的钢筋混凝土,怎么盖,怎么设计,你说了算。

                  5. 要实现第四点怎么盖,怎么设计,需要更多的实践和理论结合,才可以。

           

             小结后继续面向对象其他特性分析:

        3、抽象类abstract

  • 抽象类本身的概念就是把一些事物共性提取出来,设置为抽象基类,然后剩余的事情交给子类去扩展。
  • 用abstract关键字修饰,修饰类就是抽象类,修饰方法就是抽象方法
  • 抽象类定义的抽象方法不能有方法体;如果定义的是非抽象方法,可以有方法体,但是抽象类的目的就是为了子类扩展,一般我们在抽象类里定义非抽象方法没有什么作用,如果你设计时在抽象类里定义非抽象方法,可能是设计冗余。
  • 子类继承抽象类后,必须实现抽象方法,实现的关键字也是override
  • 抽象方法必须放在抽象类中,抽象类中允许有非抽象方法。
  • 如果子类也是抽象类,那么可以不实现抽象方法,也就是说不用override
  • 例子如下:
  • public abstract class Car
    {
    //字段_name
    private string _name;
    //属性Name
    public string Name { get; set; }
    //抽象方法
    public abstract void Fun();
    //非抽象方法F
    public void F()
    { } }
    //继承抽象类Car
    public abstract class DZCar : Car
    {
    //因为我本身也是抽象类,所以不用实现抽象方法
    public abstract void Test();
    }
    //继承抽象类DZCar
    public class SDZ:DZCar
    {
    //基类和父类都是抽象方法,继承下来后需要实现两个方法,否则报错,这就是抽象类的特点
    //实现基类方法Fun
    public override void Fun()
    { }
    //实现父类方法Test
    public override void Test()
    { }
    }

    以上例子说明了抽象类是什么,怎么定义、继承和实现抽象类

  • 那么一个很现实的问题来了,我们学会了虚方法virtual,又了解了抽象类定义abstract,这两个之间有什么区别,实际用时到底怎么区别定位呢?
  • 区别:
    • virtual abstract
      只能修饰方法,不能作为类修饰符 既可以修饰方法,也可以修饰类
      基类定义时必须有方法体 不能有方法体
      目的是为了让子类实现定义的方法,子类也可以不实现,不强制 目的是为了让子类实现,子类继承后必须实现方法体,除非子类也是虚方法
      虚方法所在的基类可以被实例化在赋值使用 抽象类不能被实例化
    • 比如我们做一个超市系统,超市里有很多种类的物品,我们经常需要设计一个录入物品的超市系统
    • 这时我发现,每件物品都会有名称和描述,那么我设计一个抽象类如下:
    • public abstract class SuperBase
      {
      private string _name;
      public string Name {
      get { return _name; }
      set
      {
      if (Name != "未分类")
      {
      _name = value;
      }
      else
      {
      _name = "未分类";
      }
      }
      }
      public abstract string Desc();
      }
      public class TypeCas : SuperBase
      { public override string Desc()
      {
      Console.WriteLine("描述下" + base.Name);
      return "";
      }
      }

      static void Main(string[] args)
      {
      TypeCas typecas = new TypeCas();
      typecas.Name = "可乐";
      typecas.Desc();

      }

      这个时候物品描述将被继承下来

    • 录入物品的时候我们发现不同的物品价钱也不是一样,那么怎么办呢?这时我就定义了一个虚方法virtual,如下例子:
    • public virtual decimal Price { get; set; }
      
       public override decimal Price
      {
      get
      {
      return base.Price;
      } set
      {
      base.Price = value;
      }
      } TypeCas typecas = new TypeCas();
      typecas.Price = ;
      //好处如果别的分类新增了,直接
      TypeCas typecas = new TypeCas2();
      TypeCas typecas = new TypeCas3();

      因为抽象类不支持实例化,子类重写后继承实现了抽象类的抽象方法,这个是大家共享的,每个子类都必须且要实现

    • 虚方法重写后是为了子类某些态度的变化,子类要有自己的态度。
    • 而超市物品描述是每个物品都有的,价钱呢经常会变动,
    • 当然如果你说我就用抽象类来定义价钱,也可以的,只是通过例子来说明这两个类型的问题
  • 里氏转换
    • 就是基类和子类之间的互相转换
    • 子类继承基类,子类可直接转换成对应基类
    • 基类可以转换为相同类型下的子类
    • 例子如下:
    •     class A
      {}
      class B:A
      {} static void Main(string[] args)
      {
      B b = new B();
      A a = b; //没问题
      B b2 = (B)a; // 可以强制转换,因为虽然是基类->子类转换,但是同类型下的可以,什么是同类型,就是说子类也转换成父类了,反过来父类可以强转子类
      A a1 = new A();
      // B b1 = (B)a1; //报错,不能强制转换
      B b1;
      //里氏转换判断
      if(a1 is B)
      {
      b1 = (B)a1;
      } b1 = a1 as B; //转换成功直接返回转换后结果,转换失败返回null
      }
  • this和base
    • this表示访问本类某字段
    • base表示访问基类某字段
  • 接口
    • 关键字修饰interface
    • 接口成员包含:方法、属性、索引、事件
    • 接口是对能力的抽象定义
    • 接口成员不能有访问修饰符、不需要方法体
public interface IFun
{
//方法
void Desc();
//属性
string Name
{
get;
set;
}
//索引
string this[int index]
{
get;
set;
}
//事件
event Action MyEvent; }
  • 实现接口时有普通实现和显示实现两种方式
    1. 普通实现就是把接口成员实现
    2. 显示实现实现时前面把对应接口名带上,解决多继承接口时成员重名问题
      1. 如果是显示实现,实例化时,必须声明接口类型来接收:如下:
static void Main(string[] args)
{
Car c = new Car(); // 这是错的
IFun i = new Car(); //这是对的
i.Desc();
}
  • 接口和抽象类之间区别:通俗的说,接口是底层设计,更注重行为,或者不影响类基本继承的一个设计,不会去要求和影响类后续的使用;
  • 抽象类已经有了基类的概念作用,类的继承要实现并且使用,已经作为一个大模板来定义了。    

说到这里,我们总结一下:

  1. 其实面向对象不就是多个特性特征组成的语法,不管是接口、抽象类、虚方法,继承,封装等等,扩展性比面向过程更高了,耦合性更低了,大家都比较独立起来了,独立更多的就说成是子类的扩展和独立。
  2. 面向对象的地基有了后,其实面向对象里的扩展和设计很多很多,可以先学习一个,经验多了,慢慢总结,不要贪多。
  3. 假如不用面向对象行不行,答案是行,可以实现功能,也可以完成项目开发,因为面向对象就是一个设计。那为什么我们不管是C#、Java,作为主流语言要面向对象,那其实是工作方面、项目方面不一样,我们开发网站系统、OA管理系统等,作为高级语言开发效率高了,面临的一个很重要的问题就是灵活定制,便于维护,那么这不就正是面向对象擅长了优点嘛,所以以我目前的经验或者后来者也是做这方面来说,面向对象对开发项目是很重要的概念+实际应用。

C#根据工作经验来谈谈面向对象的更多相关文章

  1. 一位10年Java工作经验的架构师聊Java和工作经验

    从事近十年的 JavaEE 应用开发工作,现任阿里巴巴公司系统架构师.对分布式服务架构与大数据技术有深入研究,具有丰富的 B/S 架构开发经验与项目实战经验,擅长敏捷开发模式.国内开源软件推动者之一, ...

  2. 转:一位10年Java工作经验的架构师聊Java和工作经验

    黄勇( 博客),从事近十年的 JavaEE 应用开发工作,现任阿里巴巴公司系统架构师.对分布式服务架构与大数据技术有深入研究,具有丰富的 B/S 架构开发经验与项目实战经验,擅长敏捷开发模式.国内开源 ...

  3. java后端程序员1年工作经验总结

    java后端1年经验和技术总结(1) 1.引言 毕业已经一年有余,这一年里特别感谢技术管理人员的器重,以及同事的帮忙,学到了不少东西.这一年里走过一些弯路,也碰到一些难题,也受到过做为一名开发却经常为 ...

  4. 3年java工作经验必备技能

    3年工作经验的Java程序员应该具备的技能 一.Java基础 1.String类为什么是final的. 2.HashMap的源码,实现原理,底层结构. 3.反射中,Class.forName和clas ...

  5. 一年工作经验的大专生程序员(java后台)

    1.文章前言     作为18应届毕业大专生已工作一年,相信这也是大部分同届生的现状.       那么,一个萌新进入职场一年都经历了什么呢?在校那会我是挺好奇的.       这篇文章是根据自己一年 ...

  6. 一名3年工作经验的java程序员应该具备的职业技能

    一名3年工作经验的Java程序员应该具备的技能,这可能是Java程序员们比较关心的内容.我这里要说明一下,以下列举的内容不是都要会的东西—-但是如果你掌握得越多,最终能得到的评价.拿到的薪水势必也越高 ...

  7. 精干货! Java 后端程序员 1 年工作经验总结

    一.引言   毕业已经一年有余,这一年里特别感谢技术管理人员的器重,以及同事的帮忙,学到了不少 东西.这一年里走过一些弯路,也碰到一些难题,也受到过做为一名开发却经常为系统维护 和发布当救火队员的苦恼 ...

  8. 一名3年工作经验的java程序员应该具备的技能

    一名3年工作经验的Java程序员应该具备的技能,这可能是Java程序员们比较关心的内容.我这里要说明一下,以下列举的内容不是都要会的东西—-但是如果你掌握得越多,最终能得到的评价.拿到的薪水势必也越高 ...

  9. 一年半前端工作经验试水杭州:我是如何拿下网易、阿里和滴滴 offer 的

    前言 笔者毕业于东北大学,大学毕业社招进入环球网,前端开发工程师一职.技术栈:React+node,Github 地址 成果 来到杭州的目标非常的明确,大厂.其实就是网易.阿里和滴滴.好在基本三家都拿 ...

随机推荐

  1. Linux编程实现蜂鸣器演奏康定情歌

    Linux编程实现蜂鸣器演奏康定情歌 摘自:https://blog.csdn.net/jiazhen/article/details/3490979   2008年12月10日 15:40:00 j ...

  2. Hive入门学习随笔(二)

    ====使用Load语句执行数据的导入 --将操作系统上的文件student01.txt数据导入到t2表中 load data local inpath '/root/data/student01.t ...

  3. mysql中查看表结构的sql语句

    mysql查看表结构命令,如下: desc 表名;show columns from 表名;describe 表名;show create table 表名; use information_sche ...

  4. UVALive 7752 Free Figurines (瞎搞)

    题意:给定 n 个盒子,然后告诉你每个盒子在哪个盒子里,数值越大,盒子越大,给定你初态,和末态,问你最少要几步能完成,只有两种操作,一种是把一个盒子连同里面的小盒子放到一个空盒子里,另一种是把一个堆盒 ...

  5. activiti监听器

    activiti使用的时候,通常需要跟业务紧密的结合在一起,有些业务非常的复杂,通常有如下一些场景: 1.activiti人员动态的分配. 2.当前任务节点完成的时候,指定需要指定下一个节点的处理人( ...

  6. C++11中的tuple应用:让函数返回多个值

    在没有tuple之前,如果函数需要返回多个值,则必须定义一个结构体,有了C++11,可以基于tuple直接做了,下面是个示例: // 编译:g++ -std=c++11 -g -o x x.cpp # ...

  7. NSArray去除重复元素

    直接上代码吧! 1.可以创建一个新的数组,对需要去除重复的数组进行遍历,如果新数组不包含就数组,那么添加元素,如果包含就不添加. NSMutableArray *array = [NSMutableA ...

  8. Android-ContentProvider-UriMatcher

    注意:在ContentProvider里面写对数据库增删改查的时候,千万不能 db.close();  cursor.close(); 等操作,不然其他应用访问不到数据,也没有必要写isOpen(); ...

  9. Re:从零开始的Spring Security Oauth2(一)

    前言 今天来聊聊一个接口对接的场景,A厂家有一套HTTP接口需要提供给B厂家使用,由于是外网环境,所以需要有一套安全机制保障,这个时候oauth2就可以作为一个方案. 关于oauth2,其实是一个规范 ...

  10. 把windows电脑变成路由器使用

    实用小技巧1 把windows电脑变成路由器使用 适用对象: windows7.windows8的笔记本电脑或者有无线网卡的台式电脑 网络要求: CMCC-EDU和家里拨号上网的都可以,但是电信的校园 ...