一:前言
在本系列课程的第一部分,我们说明为了要选择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();
因为子类已经new了父类的方法,故子类方法和基类方法完全没有关系了,只要s被转型为Sharp,则针对s调用的都是父类方法。
第二种用法很好理解,调用的都是子类的方法,如下所示:
Triangle triangel = new Triangle();
triangel.MethodVirtual();
triangel.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
本文转自最课程陆敏技博客园博客,原文链接:http://www.cnblogs.com/luminji/p/4368509.html,如需转载请自行联系原作者