C#面向对象(OOP)入门—第一天—多态和继承(方法重载)
面向对象是什么
面向对象是一种基于对象的编程方法,它取代了仅仅依靠方法和流程的编程方式。面向对象的编程语言中,对象(object)其实就是指特定类型、或某个类的实例。面向对象使得编程人员更容易组织和管理整个软件的程序。对象之间的独立性使得我们更容易更新和更改程序。对于大型程序则更加易读和易管理。
面向对象包含哪些内容
- 数据抽象(Data Absraction):数据抽象的概念是逻辑实现的内部和多余的细节对用户隐藏。用户可以使用一个类所允许使用的任何数据和方法,而不必要明白它是如何创建或者它背后有多么复杂。举一个现实的例子,就是开车过程中,我们可以通过换挡杆来换档,然而我们比不要知道他是如何换挡的,比如换挡过程中齿轮箱等部件是如何动作的,我们只需要根据自己的需要来切换档位就行。
- 继承(Inheritance):继承是面向对象中最受欢迎的概念,继承让开发者可以实现代码重用。例如,我们在一个类里面实现了一个有特定逻辑功能的函数,我们可以用这个类派生一个新的类,然后我们不必要重新写这个函数,在派生类中可以直接使用。
- 数据封装(Data Encapsulation):将类里面的成员函数和成员数据封装进一个单独的模块叫做封装,数据或函数的可见性可以由访问修饰符来控制。
- 消息通讯(Message Communication):指当一个对象调用一个类的方法去执行的时候,消息的通讯手段。
方法重载(编译时多态)
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);
}
}
类似上面的代码,3个函数拥有相同的名字,但是参数类型不同。这就是方法重载。
知识点:在C#中,函数签名(识别一个函数的依据)包括函数的名称以及函数的参数(包括参数数量和参数数据类型,参数的类型)。函数的返回值不是,函数的修饰关键字(比如static)也不是。参数的类型指的是ref或者out的参数。(例如 int a 和 ref int a不同,但是注意 ref int a 和 out int a 相同)。
参数数组参数在多态中的作用
一个方法被调用过程中,参数的类型有以下几种:
- 传递值
- 传递引用(ref)
- 作为输出参数(out)(其实也是一种引用的传递)
- 采用参数数组(params)
当我们传递N个(未知)参数给某个方法的时候,我们可以用params关键字。
知识点:params关键字只能在方法的最后一个参数,并且数组不能是多维的。
例如如下定义是错误的:
private void DisplayOverload(int a, params string[] parameterArray, int b) { }
知识点:当有个params的参数在最后一个,但是倒数第二个参数的类型和最后一个相同,则传递参数中,符合类型的第一个会给值,剩余的给后面的参数数组(例如 []和[][]可以,而[,] 不可以),并且params关键字不能和ref或out组合使用。
例如:
public class Overload
{
public void Display()
{
DisplayOverload(100, 200, 300); //100会传递给参数a,200和300传递给parameterArray
DisplayOverload(200, 100); //200传递给参数a,100传递给parameterArray
DisplayOverload(200); //200传递给参数a
} private void DisplayOverload(int a, params int[] parameterArray)
{
foreach (var i in parameterArray)
Console.WriteLine(i + " " + a);
} }
这种情况也是错误的
public class Overload
{
public void Display()
{
string [] names = {"Akhil","Arsh"};
DisplayOverload(2, names, "Ekta"); //当调用函数时,编译器会试图将names和 "Ekta"组合为一个字符串数组,但是names不是string而是string[]类型,所以会报错
} private void DisplayOverload(int a, params string[] parameterArray)
{
foreach (var str in parameterArray)
Console.WriteLine(str + " " + a);
} }
对于在传递数组参数后,改变其值会发生什么举如下两个例子:
例子1:
public class Overload
{
public void Display()
{
int[] numbers = {10, 20, 30};
DisplayOverload(40, numbers);
Console.WriteLine(numbers[1]);
} private void DisplayOverload(int a, params int[] parameterArray)
{
parameterArray[1] = 1000;
} } class Program
{
static void Main(string[] args)
{
Overload overload = new Overload();
overload.Display();
Console.ReadKey();
}
} //输出结果是1000
例子2:
public class Overload
{
public void Display()
{
int number = 102;
DisplayOverload(200, 1000, number, 200);
Console.WriteLine(number);
} private void DisplayOverload(int a, params int[] parameterArray)
{
parameterArray[1] = 3000;
} } class Program
{
static void Main(string[] args)
{
Overload overload = new Overload();
overload.Display();
Console.ReadKey();
}
} //输出结果是102
例子1中的数组传递到函数里面,在函数更改值对应也更改了原数组的值。(我认为是传递的数组的地址);但是对于例子2,不得不新建一个数组来存放各个参数组成的新数组,然后传递到调用的函数里,当对其更改值时,其实更改的是新数组对应的值,而这个函数对number的值一无所知,跟别提去更改了。
另一种重载的情况
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");
} } class Program
{
static void Main(string[] args)
{
Overload overload = new Overload();
overload.Display();
Console.ReadKey();
}
} /*
输出是:
parameterArray
The two integers 200 300
parameterArray
*/
第一个和第三个调用没有问题,只能调用带数组的参数。但是对于第二种我不太理解,原文作者解释说,C#很聪明,他对params参数不太认可,就像不是自己的亲生孩子一样,所以作为第二选择,两个int参数正好有匹配的函数来调用,所以这样输出。作者虽然感情化的描述了,但是可以理解,因为确实就是这样的机制。要想真正的明白缘由,或许还要查阅一些资料才能明白,暂时搁置这个问题吧。
好吧,一个更有趣的例子:
public class Overload
{
public static void Display(params object[] objectParamArray)
{
foreach (object obj in objectParamArray)
{
Console.Write(obj.GetType().FullName + " ");
}
Console.WriteLine(); }
} class Program
{
static void Main(string[] args)
{
object[] objArray = { 100, "Akhil", 200.300 };
object obj = objArray;
Overload.Display(objArray);
Overload.Display((object)objArray);
Overload.Display(obj);
Overload.Display((object[])obj);
Console.ReadKey(); }
} /*
输出结果是:
System.Int32 System.String System.Double
System.Object[]
System.Object[]
System.Int32 System.String System.Double
*/
Display函数是输出数组内每个成员的类型。第一个调用没有问题,第二个调用与第三个调用等价,传递过去的其实是一个object对象(注意这不是装箱,装箱时值类型转换为object,而数组是引用类型),这时,首先是将object转换为object[]但是不存在这样的默认转换,所以只能创建一个object[]来存放obj,而obj的类型就是object[],所以输出System.Object[]。第四个调用,做了强制的转换,从object到object[],所以跟第一个相同。
总结:
这篇文章主要讲了编译时多态,也叫作前期绑定或者方法重载。并列举了方法重载的各种情形,以及params参数在重载中的特殊用途。
- C# 通过其参数而不是由它的名字来区别一个方法。
- 方法返回值和参数类型绝不是方法签名的一部分,如果方法的名称相同。所以这不是多态性。
- 修饰符(如static)不是方法签名的一部分。
- 方法的签名包括其名称、 形参的数量和类型。一个函数的返回类型不是签名的一部分。两种方法不能具有相同的签名,并且成员也不能有同名的成员。
- 参数名称应是唯一的。我们也可以不有一个参数名称和声明的变量名称相同的功能相同。
- 如果通过值传递,变量的值传递过去,如果通过 ref 和 out,引用的地址传递过去。
- 这 params 关键字只能应用于方法的最后一个参数。因此n 个数量的参数只可以在最后。
- C# 是很聪明地承认如果倒数第二个参数,参数有相同的数据类型。
- 参数数组必须是一维数组。
注明:原文地址:https://codeteddy.com/2014/05/11/diving-in-oop-part-1-polymorphism-and-inheritanceearly-bindingcompile-time-polymorphism/
我只是在作者基础上进行了翻译以及总结,并加了一点自己的理解,希望对大家有帮助。
如有错误,敬请指证。
C#面向对象(OOP)入门—第一天—多态和继承(方法重载)的更多相关文章
- C#面向对象(OOP)入门—第二天—多态和继承(继承)
介绍: 第一天的内容主要是不同情形下的方法重载.这一部分则主要讲面向对象中继承的概念.首先用一个要点图形来定义继承. 继承 一个简单的例子: ClassA: class ClassA:ClassB { ...
- 深入理解OOP(第一天):多态和继承(初期绑定和编译时多态)
在本系列中,我们以CodeProject上比较火的OOP系列博客为主,进行OOP深入浅出展现. 无论作为软件设计的高手.或者菜鸟,对于架构设计而言,均需要多次重构.取舍,以有利于整个软件项目的健康构建 ...
- 深入浅出OOP(一): 多态和继承(早期绑定/编译时多态)
在本系列中,我们以CodeProject上比较火的OOP系列博客为主,进行OOP深入浅出展现. 无论作为软件设计的高手.或者菜鸟,对于架构设计而言,均需要多次重构.取舍,以有利于整个软件项目的健康构建 ...
- 深入理解OOP(四): 多态和继承(抽象类)
在本文中,我们讨论OOP中的热点之一:抽象类.抽象类在各个编程语言中概念是一致的,但是C#稍微有些不一样.本文中我们会通过代码来实现抽象类,并一一进行解析. 深入理解OOP(一):多态和继承(初期绑定 ...
- 深入浅出OOP(二): 多态和继承(继承)
本文是深入浅出OOP第二篇,主要说说继承的话题. 继承的介绍 在OOP中,继承有如下的定义: 继承是一种OOP的机制,用于派生继承预定义的类 在这个继承关系中,预定义的类是基类,新类是子类 继承常常用 ...
- 深入浅出OOP(三): 多态和继承(动态绑定/运行时多态)
在前面的文章中,我们介绍了编译期多态.params关键字.实例化.base关键字等.本节我们来关注另外一种多态:运行时多态, 运行时多态也叫迟绑定. 运行时多态或迟绑定.动态绑定 在C#语音中,运行时 ...
- 深入浅出OOP(四): 多态和继承(抽象类)
在本文中,我们讨论OOP中的热点之一:抽象类.抽象类在各个编程语言中概念是一致的,但是C#稍微有些不一样.本文中我们会通过代码来实现抽象类,并一一进行解析. Abstract Classes 在微软的 ...
- PHP面向对象(OOP):把对象串行化serialize()方法,__sleep()方法,__wakeup()方法
有时候需要把一个对象在网络上传输,为了方便传输,可以把整个对象转化为二进制串,等到达另一端时,再还原为原来的对象,这个过程称之为串行化(也叫序列化), 就像我们现在想把一辆汽车通过轮船运到美国去,因为 ...
- Java之面向对象例子(三) 多态,重写,重载,equals()方法和toString()方法的重写
重写(继承关系) 子类得成员方法和父类的成员方法,方法名,参数类型,参数个数完全相同,这就是子类的方法重写了父类的方法. 重载 在一个类里有两个方法,方法名是完全一样的,参数类型或参数个数不同. 例子 ...
随机推荐
- 控制Docker Compose的启动顺序的一个思路
起源 守护进程daemon 从守护进程的角度看Docker Compose Docker的解决方案 思路 代码 结果 起源 Docker Compose提供了一个depends_on参数. https ...
- java 实现多个接口 方法重名的解决办法——内部类
package com.kk.innerClass; /** * 通过内部类实现接口 * 解决多个接口中方法重名问题 * */interface Machine { void run();} clas ...
- linux 常见服务端口
Linux服务器在启动时需要启动很多系统服务,它们向本地和网络用户提供了Linux的系统功能接口,直接面向应用程序和用户.提供这些服务的程序是由运行在后台的守护进程(daemons) 来执行的.守护进 ...
- ubuntu16.04命令行模式黑屏解决办法
ubuntu16.04命令行模式黑屏解决办法 问题描述 在ubuntu上装Nvidia的显卡驱动,需要关闭图形界面才能安装驱动,但是,出现如下情况: 使用“ctrl+alt+F1”命令进入命令行界面是 ...
- centos ldap client 设定
centos 6.4 ldap server 位于ubuntu 12.04 Server上 1.安装 yum -y install openldap-clients nss-pam-ldapd 一个完 ...
- 配置静态服务器和配置nfs
一.配置Nginx 1.安装Nginx yum -y install nginx 2.编写配置文件 [root@ngix nginx]# cd /etc/nginx [root@ngix nginx] ...
- html 5 新特性
现在html 5技术是最新的html标准,掌握html 5已经变得非常重要,以下是我查看相关资料后对html 5 的新特性的总结,方便大家对比学习.html 5的新特性1.取消了一些过时的html 4 ...
- 【C++ STL】容器概要
1.容器的共通能力 1. 所有的容器都是“value”语意,而不是“reference”语意.容器进行元素的安插操作时,内部实施的都是拷贝操作,置于容器内.因此STL容器的每个元素都必须能被拷贝.如 ...
- PowerDesigner16 用例图
用例图主要用来描述角色以及角色与用例之间的连接关系.说明的是谁要使用系统,以及他们使用该系统可以做些什么.一个用例图包含了多个模型元素,如系统.参与者和用例,并且显示这些元素之间的各种关系,如泛化.关 ...
- PHP系统编程--03.PHP进程信号处理
PHP的pcntl扩展提供了信号处理的功能,利用它可以让PHP来接管信号的处理,在开发服务器端守护进程方面,信号处理至关重要. 函数原型 bool pcntl_signal(int $signo ,c ...