深入浅出OOP(一): 多态和继承(早期绑定/编译时多态)

简介:

在本系列中,我们以CodeProject上比较火的OOP系列博客为主,进行OOP深入浅出展现。

无论作为软件设计的高手、或者菜鸟,对于架构设计而言,均需要多次重构、取舍,以有利于整个软件项目的健康构建,有些经验是前辈总结的,我们拿来使用即可,有些是团队知识沉淀的,总之复用前人好的思想有利于减少返工。当然,在面试的时候,如果能围绕OOP大谈特谈,自然会加分多多的。

开始阅读本系列博客的预备知识,多态、封装、面向对象编程等,请通过MSDN学习。如下图的术语,您应该耳熟能详的。本系列文章使用C#作为唯一脚本语言。

image

OOP

1 是什么OOP,以及OOP的优势是什么?

OOP代表的是面向对象编程(Object-Oriented Programming),它基于对象的整体进行编程,取代了基于过程函数的编程思想。具体实现是围绕对象进行数据、函数封装,而不是基于逻辑关系。OOP中的对象直达的是一个特定的类型、或者某类型的实例对象、更多时候是一个class。每个class对象的结构基本相似,但是有各自特有的属性和数据值。对象之间可通过对外的接口进行访问:方法、属性等。基于OOP的这些优势,独立的对象可以修改而不会影响到其他对象,这样会比较方便的升级软件减少潜在的bug。软件系统随着时间的推移,会变得越来越大,OOP编程思想有效的提高了系统代码的可读性和管理性。

 

image

2 OOP的概念是什么?

下面用5个术语来说明OOP的具体概念是什么:

  • 数据抽象(Data Abstraction):数据抽象是对需要操作的物体进行建模的出发点,既对使用对象进行了抽象,隐藏了内部的细节(对使用的最终用户而言)。用户可以非常方便的使用class的方法、数据,而不用关心数据创建、运行逻辑的背后复杂的过程。我们以真实世界为例,当你骑一辆自行车的时候,不用考虑变速齿轮的原理如何驱动链条、车轮吧。

  • 继承(Inheritance):继承是OOP概念中最流行的一个概念。继承给程序员提供了可复用代码的优势。基类定义好函数逻辑,子类通过继承,可实现直接访问--就想子类自身的方法一样方便。

  • 数据封装(Data Encapsulation):对class的成员变量、成员函数通过访问控制符进行包装,则称为数据封装。访问控制符有public、Protected、Private、Internal 4种类型。

  • 多态(Polymorphism):对象可通过传递不同参数实现相同的动作,这种行为我们称之为多态。我们以真实世界为例,“开车”这个方法,对不同类型的用户要提供不同的参数实现多态,如Car.Drive(Man), Car.Drive(Woman)等。

  • 消息通信(Message Communication):消息通信意味着通过通过消息进行class函数的调用、执行。

 

3 多态(Polymorphism)

在本节,我们分别用代码片段来阐述各自类型的多态类型:函数重载、早期绑定、编译器的多态。

先创建一个console 工程,并命名为InheritanceAndPolymorphism,然后添加类Overload.cs,再添加DisplayOverload函数。

public class Overload
    {        public void DisplayOverload(int a){
            System.Console.WriteLine("DisplayOverload " + a);
        }        public void DisplayOverload(string a){
            System.Console.WriteLine("DisplayOverload " + a);
        }        public void DisplayOverload(string a, int b){
            System.Console.WriteLine("DisplayOverload " + a + b);
        }
    }

Program.cs添加如下代码:

class Program
    {        static void Main(string[] args)
        {
            Overload overload = new Overload();
            overload.DisplayOverload(100);
            overload.DisplayOverload("method overloading");
            overload.DisplayOverload("method overloading", 100);
            Console.ReadKey();

 

运行程序,结果如下:

DisplayOverload 100 
DisplayOverload method overloading     
DisplayOverload method overloading100

Overload类中的DisplayOverload提供了3类不同的重载函数:方法名相同,参数类型和个数不同。C#中的这种方式成为重载,既我们不需要为每类函数定义不同名字的函数,仅需要改变函数参数类型和个数即可实现,这个也成为函数签名。

用不同的返回值可以否? 我们试试下面的代码:

public void DisplayOverload() { }public int DisplayOverload(){ }

肯定的结果是,Visual Studio会给予如下的报错信息:

Error: Type 'InheritanceAndPolymorphism.Overload' already defines a member called 'DisplayOverload' with the same parameter types

从上面的结果可知:返回值不作为多态函数签名。

 

我们再运行如下的代码:

static void DisplayOverload(int a)  {   }public void DisplayOverload(int a) {   }public void DisplayOverload(string a){  }

 

结果依然是报错:

Error: Type 'InheritanceAndPolymorphism.Overload' already defines a member called 'DisplayOverload' with the same parameter types

结论:static的可见函数修饰符不作为重载签名。

 

运行下面的代码,试试out、ref可否作为重载签名。

private void DisplayOverload(int a) {   }private void DisplayOverload(out int a)
        {
            a = 100;
        }private void DisplayOverload(ref int a) {   }

 

结果是如下的报错:

Error: Cannot define overloaded method 'DisplayOverload' because it differs from another method only on ref and out

结论:ref、out传递参数修饰符也不能作为重载签名。

 

4 多态中Params 参数的作用

一个函数可包含如下4种类型的参数传递:

  • 值传递 (pass by value)

  • 引用传递 (Pass by reference)

  • 作为output参数 (As an output parameter)

  • 使用参数数组 (Using parameter arrays)

 

我们运行如下代码:

public void DisplayOverload(int a, string a)  {   }        public void Display(int a)
        {            string a;
        }

 

不出意外,获得如下报错信息:

Error1: The parameter name 'a' is a duplicate

Error2: A local variable named 'a' cannot be declared in this scope because it would give a different meaning to 'a', which is already used in a 'parent or current' scope to denote something else

 

在相同的作用域中,参数名称必须是唯一的。

 

在Overload.cs文件中,添加如下代码:

public class Overload
    {        private string name = "Akhil";        public void Display()
        {
            Display2(ref name, ref name);
            System.Console.WriteLine(name);
        }        private void Display2(ref string x, ref string y)
        {
            System.Console.WriteLine(name);
            x = "Akhil 1";
            System.Console.WriteLine(name);
            y = "Akhil 2";
            System.Console.WriteLine(name);
            name = "Akhil 3";
        }
    }

在Program.cs中添加如下代码:

class Program
    {        static void Main(string[] args)
        {
            Overload overload = new Overload();
            overload.Display();
            Console.ReadKey();
        }
    }

运行结果如下:

Akhil 
Akhil 1     
Akhil 2     
Akhil3

image

结论:我们通过ref引用传递了name的内存地址,故修改x、y的值相当于直接修改name的值,故结果运行如上。

 

下面这段代码演示了params关键字的作用:

在Overload.cs文件添加如下代码:

public class Overload
    {        public void Display()
        {
            DisplayOverload(100, "Akhil", "Mittal", "OOP");
            DisplayOverload(200, "Akhil");
            DisplayOverload(300);
        }        private void DisplayOverload(int a, params string[] parameterArray)
        {            foreach (string str in parameterArray)
               Console.WriteLine(str + " " + a);
        }
    }

在Program.cs文件添加如下代码:

class Program
    {        static void Main(string[] args)
        {
            Overload overload = new Overload();
            overload.Display();
            Console.ReadKey();
        }
    }

运行结果如下:

Akhil 100 
Mittal 100     
OOP 100     
Akhil 200

C#提供了params动态参数数组机制,非常方便的在运行时动态传递不同数量的同类型参数。

注:params关键词仅能作为函数的最后一个参数适用。

 

我们再试试params关键字的函数签名和非params关键字函数签名的优先级顺序:

public class Overload
    {        public void Display()
        {
            DisplayOverload(200);
            DisplayOverload(200, 300);
            DisplayOverload(200, 300, 500, 600);
        }        private void DisplayOverload(int x, int y)
        {
            Console.WriteLine("The two integers " + x + " " + y);
        }        private void DisplayOverload(params int[] parameterArray)
        {
            Console.WriteLine("parameterArray");
        }

    }

Program.cs文件添加如下代码:

class Program
    {        static void Main(string[] args)
        {
            Overload overload = new Overload();
            overload.Display();
            Console.ReadKey();
        }
    }

运行结果如下:

parameterArray 
The two integers 200 300     
parameterArray

从运行结果看,C#非常巧妙的进行非params函数的精准匹配优先,如1个int类型\3个int类型,则用params类型匹配;2个int类型,用明确定义的函数进行匹配。

 

5 结论

image

在本节中,我们进行OOP系列的第一篇,主要说明了编译器的多态,它也称为早期绑定或者方法重载。同时,我们也学习C#中威力强大的params关键字,并用它来实现多态。

本文要点归纳如下:

  • C#函数重载的签名规则是用参数的类型和数量判断,而不是函数的名字。

  • 函数返回值不作为重载签名。

  • 修饰符不作为签名的一部分,如static

  • 同函数中,多个参数名称要唯一

  • ref、out是引用传递,传递的是参数的内存地址

  • params 作为参数关键词,仅能用于函数的最后一个参数


本文转自 powertoolsteam 51CTO博客,原文链接:http://blog.51cto.com/powertoolsteam/1627540,如需转载请自行联系原作者
相关文章
|
7月前
|
JavaScript 前端开发
js开发:请解释原型继承和类继承的区别。
JavaScript中的原型继承和类继承用于共享对象属性和方法。原型继承通过原型链实现共享,节省内存,但不支持私有属性。
54 0
|
7月前
|
Python
请简述Python中的继承、封装和多态的概念。
【2月更文挑战第24天】【2月更文挑战第82篇】请简述Python中的继承、封装和多态的概念。
44 1
|
搜索推荐 Java
【JavaSE专栏59】方法重写的概念及优先级问题,面向对象的多态性机制
【JavaSE专栏59】方法重写的概念及优先级问题,面向对象的多态性机制
|
6月前
|
Java
JavaSE——面向对象高级二(1/4)-面向对象三大特征之三-多态(认识多态、使用多态的好处、多态下的类型转换问题)
JavaSE——面向对象高级二(1/4)-面向对象三大特征之三-多态(认识多态、使用多态的好处、多态下的类型转换问题)
36 0
|
6月前
|
Java
JavaSE——面向对象高级一(3/4)-面向对象三大特征之二:继承(初步认识继承、了解继承的好处)
JavaSE——面向对象高级一(3/4)-面向对象三大特征之二:继承(初步认识继承、了解继承的好处)
26 0
|
7月前
|
编译器 C语言 C++
【C++成长记】C++入门 | 类和对象(上) |面向过程和面向对象初步认识、类的引入、类的定义、类的访问限定符及封装
【C++成长记】C++入门 | 类和对象(上) |面向过程和面向对象初步认识、类的引入、类的定义、类的访问限定符及封装
|
7月前
|
Serverless PHP
当谈论面向对象编程时,这四个概念是非常重要的。以下是对接口、继承、封装和多态的简要说明
本文介绍了面向对象编程的四个核心概念:接口、继承、封装和多态。接口定义对象的行为规范,类通过实现接口确保符合所需行为。继承允许在已有类基础上创建新类,实现代码重用。封装是将数据和操作捆绑并隐藏内部细节,提高安全性和可维护性。多态使对象在运行时能表现出不同行为,增加代码灵活性和可扩展性。文中还提供了代码示例来说明这些概念。
40 0
|
7月前
|
Java 数据安全/隐私保护 开发者
从零到一:深入理解Java中的封装、继承与多态
从零到一:深入理解Java中的封装、继承与多态
487 0
|
7月前
|
Java
Java面向对象编程,解释封装、继承和多态的概念。
Java面向对象编程,解释封装、继承和多态的概念。
80 2
|
7月前
|
Java
Java面向对象编程,构造函数和方法的区别是什么?
Java面向对象编程,构造函数和方法的区别是什么?
119 2