面向对象的基本概念
面向对象的基本概念有对象、类、抽象、封装、继承、多态、接口、消息、组件、模式和复用等
对象
对象是由数据及其操作所构成的封装体,是系统中用来描述客观事物的一个封装,是构成系统的基本单位,采用计算机语言描述,对象是由一组属性和对这组属性进行操作的一组服务构成。
对象包含三个基本要素,分别是对象标识、对象状态和对象行为。
每一个对象必须有一个名字以区别于其他对象,这就是对象标识;状态用来描述对象的某些特征;对象行为用来封装对象所拥有的业务操作。
举例说明,对于教师Joe而言,包含性别、年龄、职位等个人状态信息,同时还具有授课的行为特征,那么Joe就是封装后的一个典型对象。
类
类是现实世界中实体的形式化描述,类将该实体的数据和函数封装在一起。类的数据也叫属性、状态或特征,它表现类静态的一面。类的函数也叫功能、操作或服务,它表现类动态的一面。
Joe是一名教师,也就拥有了教师的特征,这个特征就是教师这个类所特有的,具体而言,共同的状态通过属性表现出来,共同的行为通过操作表现出来,如图所示。
类和对象的关系可以总结为:
- 每一个对象都是某一个类的实例。
- 每一个类在某一时刻都有零或更多的实例。
- 类是静态的,它们的存在、语义和关系在程序执行前就已经定义好了,对象是动态的,它们在程序执行时可以被创建和删除。
- 类是生成对象的模板。
抽象
抽象是通过特定的实例抽取共同特征以后形成概念的过程。它强调主要特征,忽略次要特征。一个对象是现实世界中一个实体的抽象,一个类是一组对象的抽象,抽象是一种单一化的描述,它强调给出与应用相关的特性,抛弃不相关的特性。
抽象类abstract class 。就是你把你以后需要用到的方法先写出来,就相当于 一本书的目录一样。具体内容,需要到书中指定页面才可以看到。而程序,具体的实现是有它的子类来实现的。即子类继承抽象类。
封装
封装是将相关的概念组成一个单元,然后通过一个名称来引用它。面向对象封装是将数据和基于数据的操作封装成一个整体对象,对数据的访问或修改只能通过对象对外提供的接口进行。
继承
继承表示类之间的层次关系,这种关系使得某类对象可以继承另外一类对象的特征(attributes)和能力(operations),继承又可分为单继承和多继承,单继承是子类只从一个父类继承,而多继承中的子类可以从多于一个的父类继承,Java是单继承的语言,而C++允许多继承。
假设类B继承类A,即类B中的对象具有类A的一切特征(包括属性和操作)。
类A称为基类或父类或超类,类B称为类A的派生类或子类,类B在类A的基础上还可以有一些扩展。
如图所示,Dog和Cat类都是从Mammal继承而来,具有父类的eyeColor属性特征,因此在子类中就不用重复指定eyeColor这个属性。
多态
多态性是一种方法,这种方法使得在多个类中可以定义同一个操作或属性名,并在每个类中可以有不同的实现。多态性使得一个属性或变量在不同的时期可以表示不同类的对象。
如图所示,Rectangle和Circle都继承于Shape,对于Shape而言,会有getArea0的操作。但显而易见,RectaI y;le和Circle的getArea()方法的实现是完全不一样的,这就体现了多态的特征。
接口
所谓接口就是对操作规范的说明。接口只是说明操作应该做什么( What),但没有定义操作如何做( How)。接口可以理解成为类的一个特例,它只规定实现此接口的类的操作方法,而把真正的实现细节交由实现该接口的类去完成。
接口在面向对象分析和设计过程中起到了至关重要的桥梁作用,系统分析员通常先把有待实现的功能封装并定义成接口,而后期程序员依据此接口进行编码实现。
消息
消息(Message)是对象间的交互手段,其形式如下:
Message: [dest.op,paraJ
其中dest指目标对象Destination Object,op指操作Operation,para指操作需要的参数Parameters。
组件
组件是软件系统可替换的、物理的组成部分,它封装了实现体(实现某个职能).并提供了一组接口的实现方法。可以认为组件是一个封装的代码模块或大粒度的运行对的模块,也可将组件理解为具有一定功能、能够独立工作或同其他组件组合起来协词工作的对象。
对于组件,应当按可复用的要求进行设计、实现、打包、编写文档。组件应当是内聚的,并具有相当稳定的公开的接口。
为了使组件更切合实际、更有效地被复用,组件应当具有“可变性”(variability),以提高其通用性。组件应向复用者提供一些公共“特性”,另一方面还要提供可变的“特性”。
针对不同的应用系统,只需对其可变部分进行适当的调节,复用者要根据复用的具体需要,改造组件的可变“特性”,即“客户化”。
模式
模式是一条由三部分组成的规则,它表示了一个特定环境、一个问题和一个解决方案之间的关系。每一个模式描述了一个不断重复发生的问题,以及该问题的解决方案。这样就能一次又一次地使用该方案而不必做重复劳动。
将设计模式引入软件设计和开发过程的目的在于充分利用已有的软件开发经验,这是因为设计模式通常是对于某一类软件设计问题的可重用的解决方案。
设计模式使得人们可以更加简单和方便地去复用成功的软件设计和体系结构,从而能够帮助设计者更快更好地完成系统设计。
复用
软件复用是指将已有的软件及其有效成分用于构造新的软件或系统。组件技术是软件复用实现的关键。
————————————————————————————–
C# 封装
封装 被定义为”把一个或多个项目封闭在一个物理的或者逻辑的包中”。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。
抽象和封装是面向对象程序设计的相关特性。抽象允许相关信息可视化(理解为书籍目录),封装则使开发者实现所需级别的抽象。
C# 封装根据具体的需要,设置使用者的访问权限,并通过访问修饰符来实现。
一个 访问修饰符 定义了一个类成员的范围和可见性。C# 支持的访问修饰符如下所示:
- Public:所有对象都可以访问;
- Private:对象本身在对象内部可以访问;
- Protected:只有该类对象及其子类对象可以访问;
- Internal:同一个程序集的对象可以访问;
- Protected:一个程序集内的对象,或者该类对象及其子类对象可以访问;
Public 访问修饰符
Public访问修改符允许一个类将其成员变量和成员函数暴露给其他的函数和对象。任何公有成员可以被外部的类访问。
using System;
namespace RectangleApplication
{
class Rectangle
{
//成员变量
public double length;
public double width;
public double getArea()
{
return length * width;
}
public void Display()
{
Console.WriteLine("Length:{0}", length);
Console.WriteLine("Width:{0}", width);
Console.WriteLine("Area:{0}", getArea());
}
} //Rectangle 结束
class ExecuteRectangle
{
static void Main(string[] args)
{
Rectangle r = new Rectangle();
r.length = 4.5;
r.width = 3.5;
r.Display();
Console.ReadLine();
}
}
}
在上面的实例中,成员变量 length 和 width 被声明为 public,所以它们可以被函数 Main() 使用 Rectangle 类的实例 r 访问。
成员函数 Display() 和 GetArea() 可以直接访问这些变量。
成员函数 Display() 也被声明为 public,所以它也能被 Main() 使用 Rectangle 类的实例 r 访问。
Private 修饰符
Private 访问修饰符允许一个类将其成员变量和成员函数对其他的函数和对象进行隐藏。
只有同一个类中的函数可以访问它的私有成员。即使是类的实例也不能访问它的私有成员。
using System;
namespace RectangleApplication
{
class Rectangle
{
private double length;
private double width;
public void Acceptdetails()
{
length = Convert.ToDouble(Console.ReadLine());
width = Convert.ToDouble(Console.ReadLine());
}
public double getArea()
{
return length * width;
}
public void Display()
{
Console.WriteLine("Length:{0}", length);
Console.WriteLine("Width:{0}", width);
Console.WriteLine("Area:{0}", getArea());
}
}
class ExecuteRectangle
{
static void Main(string[] args)
{
Rectangle r = new Rectangle();
r.Acceptdetails();
r.getArea();
r.Display();
Console.ReadLine();
}
}
}
在上面的实例中,成员变量 length 和 width 被声明为 private,所以它们不能被函数 Main() 访问。
成员函数 AcceptDetails() 和 Display() 可以访问这些变量。
由于成员函数 AcceptDetails() 和 Display() 被声明为 public,所以它们可以被 Main() 使用 Rectangle 类的实例 r 访问。
Protected 访问修饰符
Protected 访问修饰符允许子类访问它的基类的成员变量和成员函数,这样有助于实现继承。
Internal 访问修饰符
Internal 访问说明符允许一个类将其成员变量和成员函数暴露给当前程序中的其他函数和对象。换句话说,带有 internal 访问修饰符的任何成员可以被定义在该成员所定义的应用程序内的任何类或方法访问。
using System;
namespace RectangleApplication
{
class Rectangle
{
internal double length;
internal double width;
double getArea()
{
return length * width;
}
public void Display()
{
Console.WriteLine("Length:{0}", length);
Console.WriteLine("Width:{0}", width);
Console.WriteLine("Area:{0}", getArea());
}
}
class ExecuteRectangle
{
static void Main(string[] args)
{
Rectangle r = new Rectangle();
r.length = 4.5;
r.width = 3.5;
r.Display();
Console.ReadLine();
}
}
}
在上面的实例中,请注意成员函数 GetArea() 声明的时候不带有任何访问修饰符。如果没有指定访问修饰符,则使用类成员的默认访问修饰符,即为 private。
Protected Internal 修饰符
Protected Internal 访问修饰符允许在本类,派生类或者包含该类的程序集中访问。这也被用于实现继承。
————————————————————————————–
C# 方法
一个方法是把一些相关的语句组织在一起,用来执行一个任务的语句块。每一个 C# 程序至少有一个带有 Main 方法的类。
要使用一个方法,您需要:
定义方法
调用方法
C# 中定义方法&调用方法
当定义一个方法时,从根本上说是在声明它的结构的元素。在 C# 中,定义方法的语法如下:
<Access Specifier> <Return Type> <Method Name>(Parameter List)
{
Method Body
}
下面是方法的各个元素:
Access Specifier:访问修饰符,这个决定了变量或方法对于另一个类的可见性。
Return type:返回类型,一个方法可以返回一个值。返回类型是方法返回的值的数据类型。如果方法不返回任何值,则返回类型为 void。
- Method name:方法名称,是一个唯一的标识符,且是大小写敏感的。它不能与类中声明的其他标识符相同。
- Parameter list:参数列表,使用圆括号括起来,该参数是用来传递和接收方法的数据。参数列表是指方法的参数类型、顺序和数量。参数是可选的,也就是说,一个方法可能不包含参数。
- Method body:方法主体,包含了完成任务所需的指令集。
实例——下面的代码片段显示一个函数 FindMax,它接受两个整数值,并返回两个中的较大值。它有 public 访问修饰符,所以它可以使用类的实例从类的外部进行访问。
using System;
namespace FunctionApplication
{
class NumberManipulator
{
//方法的定义
public int FindMax(int num1, int num2)
{
return num1>num2?num1:num2;
}
//方法的调用
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
int num1 = 10;
int num2 = 20;
int ret = n.FindMax(num1, num2);
Console.WriteLine("Max:{0}", ret);
Console.ReadLine();
}
}
}
您也可以使用类的实例从另一个类中调用其他类的公有方法。例如,方法 FindMax 属于 NumberManipulator 类,您可以从另一个类 Test 中调用它。
using System;
namespace CaculatorApplication
{
class NumberManipulator
{
public int FindMax(int num1, int num2)
{
return num1>num2?num1:num2;
}
}
class Test
{
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
int num1 = 23;
int num2 = 34;
int ret = n.FindMax(num1, num2);
Console.WriteLine("Max:{0}", ret);
Console.ReadLine();
}
}
}
递归方法调用
一个方法可以自我调用。这就是所谓的 递归。下面的实例使用递归函数计算一个数的阶乘:
using System;
namespace CaculatorApplication
{
class NumberManipulator
{
public int factorial(int num)
{
/*局部变量定义*/
int result;
if (1 == num)
{
return 1;
}
else
{
result = factorial(num - 1) * num;
return result;
}
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
Console.WriteLine("factorial(3):{0}", n.factorial(3));
Console.ReadLine();
}
}
}
参数传递
当调用带有参数的方法时,您需要向方法传递参数。在 C# 中,有三种向方法传递参数的方式:
按值传递参数
这是参数传递的默认方式。在这种方式下,当调用一个方法时,会为每个值参数创建一个新的存储位置。
实际参数的值会复制给形参,实参和形参使用的是两个不同内存中的值。所以,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。下面的实例演示了这个概念:
using System;
namespace CaculatorApplication
{
class NumberManipulator
{
public void Swap(int a, int b)
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
Console.WriteLine("a:{0}\tb:{1}", a, b);
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
int a = 10;
int b = 20;
n.Swap(a, b);
Console.WriteLine("a:{0}\tb:{1}", a, b);
Console.ReadLine();
}
}
}
按引用传递参数
引用参数是一个对变量的内存位置的引用。当按引用传递参数时,与值参数不同的是,它不会为这些参数创建一个新的存储位置。引用参数表示与提供给方法的实际参数具有相同的内存位置。
在 C# 中,使用 ref 关键字声明引用参数。下面的实例演示了这点:
using System;
namespace CaculatorApplication
{
class NumberManipulator
{
public void Swap(ref int a, ref int b)
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
int a = 10;
int b = 20;
n.Swap(ref a, ref b);
Console.WriteLine("a:{0}\tb:{1}", a, b);
Console.ReadLine();
}
}
}
按输出传递参数
return 语句可用于只从函数中返回一个值。但是,可以使用 输出参数 来从函数中返回两个值。输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。
using System;
namespace CaculatorApplication
{
class NumberManipulator
{
public void getValue(out int x)
{
int temp = 5;
x = temp;
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
int a = 10;
Console.WriteLine("调用之前a:{0}", a);
n.getValue(out a);
Console.WriteLine("调用之后a:{0}", a);
Console.ReadLine();
}
}
}
注意方法定义时方法的返回值类型,方法调用时方法参数中的out。
当上面的代码被编译和执行时,它会产生下列结果:
在方法调用之前,a 的值: 10
在方法调用之后,a 的值: 5
提供给输出参数的变量不需要赋值。当需要从一个参数没有指定初始值的方法中返回值时,输出参数特别有用。请看下面的实例,来理解这一点:
using System;
namespace CaculatorApplication
{
class NumberManipulator
{
public void getValue(out int x, out int y)
{
x = Convert.ToInt32(Console.ReadLine());
y = Convert.ToInt32(Console.ReadLine());
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
int a, b;
n.getValue(out a, out b);
Console.WriteLine("a:{0}\tb:{1}", a, b);
Console.ReadLine();
}
}
}
————————————————————————————–
C# 可空类型(Nullable)
C# 可空类型(Nullable)
C# 提供了一个特殊的数据类型,nullable 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值。
例如,Nullable< Int32 >,读作”可空的 Int32”,可以被赋值为 -2,147,483,648 到 2,147,483,647 之间的任意值,也可以被赋值为 null 值。类似的,Nullable< bool > 变量可以被赋值为 true 或 false 或 null。
在处理数据库和其他包含可能未赋值的元素的数据类型时,将 null 赋值给数值类型或布尔型的功能特别有用。例如,数据库中的布尔型字段可以存储值 true 或 false,或者,该字段也可以未定义。
声明一个 nullable 类型(可空类型)的语法如下:
< data_type> ? <variable_name> = null;
下面的实例演示了可空数据类型的用法:
using System;
namespace CalculatorApplication
{
class NullablesAtShow
{
static void Main(string[] args)
{
int? num1 = null;
int? num2 = 45;
double? num3 = new double?();
double? num4 = 3.14157;
bool? boolval = new bool?();
// 显示值
Console.WriteLine("显示可空类型的值: {0}, {1}, {2}, {3}",
num1, num2, num3, num4);
Console.WriteLine("一个可空的布尔值: {0}", boolval);
Console.ReadLine();
}
}
}
Null 合并运算符( ?? )
Null 合并运算符用于定义可空类型和引用类型的默认值。Null 合并运算符为类型转换定义了一个预设值,以防可空类型的值为 Null。Null 合并运算符把操作数类型隐式转换为另一个可空(或不可空)的值类型的操作数的类型。
如果第一个操作数的值为 null,则运算符返回第二个操作数的值,否则返回第一个操作数的值。下面的实例演示了这点:
using System;
namespace CalculatorApplication
{
class NullablesAtShow
{
static void Main(string[] args)
{
double? num1 = null;
double? num2 = 3.14157;
double num3;
num3 = num1 ?? 5.34;
Console.WriteLine("num3 的值: {0}", num3);
num3 = num2 ?? 5.34;
Console.WriteLine("num3 的值: {0}", num3);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
num3 的值: 5.34
num3 的值: 3.14157
————————————————————————————–
学习参考:
1、https://docs.microsoft.com/en-us/dotnet/articles/csharp/index
2、http://blog.youkuaiyun.com/hxtan/article/details/17149221