l 关于面向对象
l 类和对象
l 面向对象三大特性
l 访问修饰符和访问约束
l 多态的实现
1.关于面向对象
面向对象是一种思想,一种编程方式,它区别于早期的面向过程编程,通过将功能单一化,模块化,使得应用程序的开发过程更容易达到低耦合高内聚的特点,从而更好的实现代码重用和扩展。面向对象编程有封装,继承,多态三个主要特性。
2.类和对象
类是面向对象的核心之一,它是一个模具,一种数据类型,是不同数据交互的集合,也是实现面向对象的主要介质,它是抽象的概念,不能直接应用。
对象是类的实例,如果说类是面向对象的实现介质,那对象就可以看做具体的实现方法,对象依托于类产生,它有具体的数据属性等,在面向对象编程过程中会经常使用到。
3.面向对象特性之——封装
面向对象基于其三个特性而凸显其编程优势,封装便是实现面向对象编程的基础。通过对很多经常用到的单一功能模块及某些特殊功能模块进行封装,我们可以在之后的使用过程中直接调用,提高编码效率,减少无意义的重复。而在具体的编程概念中,对封装的理解体现的更加具体,它包括:
1)将类中的数据成员(字段,数组)设置成私有,以保护其安全性,同时通过一个公开的属性或索引器来使得外部可以访问到该成员以完成对其读、写。
2)将代码封装在具体的方法中,这在编程中是一种最为常见的封装,封装之后,我们在需要该方法的功能时可以直接调用该方法而无需再写其具体的执行代码。
3)将变量和方法封装在一个类中。项目的使用,很少会只在一个类中进行,而对于面向对象编程,为了提高程序的可扩展性和方便维护,经常会将不同的数据成员定义在不同的类中。
4)将类封装到命名空间下。命名空间可以看做是对类的逻辑划分,不同的命名空间内可以定义相同的类名,通过实现封装,解决了多个类中类名重复使用的问题。
5)将类的代码封装到一个程序集中。通过将编写好的类封装在程序集中,我们可以在之后的使用中直接引用该程序集,从而完成功能的嫁接。
4.面向对象特性之——继承
继承描述了类与类的一种关系,通过继承,派生类可以获得基类的所有非私有数据(构造函数也不会继承),包括基类的属性,方法函数等,如此一来,派生类也间接的获得基类实现的某些功能而无需再重复编写代码。在C#中类与类之间的继承是单继承,一个类无法同时从多个类中继承,但可以同时被多个类继承。
继承具有传递性,也就是说从某个类从其基类继承过来的数据或功能可以被其子类继承。所有的类都直接或间接继承自Object类,在子类中访问基类成员需要使用base关键字。
5.面向对象特性之——多态
面向对象的核心思想,即基于封装和继承的基础,实现代码功能多样化,减少代码重复,实现成员之间的灵活调用。实现多态有两种方式,一种是基于继承实现多态,一种是基于接口实现多态,它们都是遵循里氏替换原则而调用的。
基于继承实现多态:
子类从父类中继承,在获得父类成员的同时也可以拓展自身的数据成员,可以重写父类中用virtual关键字修饰的虚方法或必须重写用abstract关键字修饰的抽象方法,基于里氏替换原则,可以通过声明父类的变量指向子类的对象,从而达到根据不同要求调用不同子类成员的目的。
基于接口实现多态:
接口可以看做一种约束或者规范,也可以看做是某些功能的代码表现,接口不提供具体实现,其内的方法只有签名部分,无方法体。类通过实现接口,解决了只能继承自一个类的局限性,接口之间可以多继承。类实现接口,基于里氏替换原则,可以用接口变量指向实现接口的类的对象,调用不同类中实现的接口的功能。
接口实现多态的真正意义在于,接口在定义单一功能的同时,接口之间又可以多继承。我们可以在不改变原有代码的基础上,通过新增接口或者综合接口,在完成对程序的拓展的同时又不改变其兼容性。
6.访问修饰符与访问级别约束
Public 任何地方都可以访问
Internal 同一个程序集内可以访问
Protected 本类及其子类中可以访问
Private 本类中可以访问
命名空间内的默认访问级别时internal,类和结构中的默认访问级别是private, 枚举和接口中的默认访问级别时public(接口中的方法等不能写访问修饰符)。
约束:
子类的访问级别不能高于父类;当类中的成员访问级别高于类时,成员的访问级别隐式为类的访问级别;定义的变量的实际访问级别(即使字段的定义访问修饰级别高过类型,它的访问级别依然会受到其所在的类的影响)不能比类型的访问级别高;方法的实际访问级别不能比方法的参数类型和返回值级别高。
7.字段和数组的封装
字段的封装通过属性来实现,数组的封装通过索引器来实现。通过封装,我们可以在保护数据安全的同时,又能从外部访问到,完成读写等操作。
下面是关于二者的两个实例:
class Program
{
static void Main(string[] args)
{
Person p = new Person();
p.Name = "钱多多";
string names = null;
for (int i = 0; i <p.Count ; i++)
{
names += p[i] + "\t";
}
Console.WriteLine("{0}\t{1}",p.Name,names);
Console.ReadKey();
}
}
class Person
{
//只读属性
public int Count
{
get
{
return IDs.Count();
}
}
//字段和属性
string name;
public string Name
{
get { return name; }
set
{
if (value == "钱多多")
{
name = "真土豪";
}
else
{
name = value;
}
}
}
//数组和索引器
string[] IDs = {"酷酷鼠","跳跳龙","偷吃猫","懒羊羊" };
public string this[int index]
{
get { return IDs[index]; }
set { IDs[index] = value; }
}
}
8.构造方法
构造方法是类中独特的函数成员,其方法名与类名相同,无返回值部分,通常用于完成类中数据成员的初始化。默认情况下,我们不显式写出构造方法时,编译器会通过默认构造方法(无参数,不可见)完成类的数据成员的初始化,当我们显式写出构造方法时,默认构造方法会自动消失,类中数据成员的初始化将由显式构造函数完成。
构造方法可以重载,可以在构造方法后加this关键字及相应参数调用本类对应参数的构造方法,在有基类时也可以通过base关键字及相应参数调用父类中对应参数的构造方法。
子类中构造方法的执行,若构造方法后面没有this或base关键字,其执行会先调用父类的无参构造方法。若有this或base关键字,则根据参数指定调用本类或基类中对应的构造方法,所以其执行顺序,即其初始化顺序,是由父类先开始的。所以一般在代码编写中,为了防止子类没有明确调用对应参数的构造方法而出错,应该在父类中写一个无参构造方法。
9.虚方法、抽象方法
虚方法和抽象方法,应用于面向对象中基于继承的多态的实现。虚方法用virtual关键字修饰,抽象方法用abstract关键字修饰,二者的一些相同点和不同点如下:
相同:
1)虚方法和静态方法都不能用static修饰
2)虚方法和静态方法都不能是私有的(private修饰)
不同:
1)虚方法需要写出方法的实现体而抽象方法不能写方法体。
2)抽象方法必须写在抽象类中而虚方法没有要求。
3)父类的抽象成员子类必须重写,除非子类也是抽象类
4)抽象类不能实例化
抽象方法通过继承实现多态的例子:
class Program
{
//要实现U盘、MP3播放器、移动硬盘三种移动存储设备,要求计算机能同这三种
//设备进行数据交换,并且以后可能会有新的第三方的移动存储设备,所以计算机
//必须有扩展性,能与目前未知而以后可能会出现的存储设备进行数据交换。各个存储
//设备间读、写的实现方法不同,U盘和移动硬盘只有这两个方法,MP3Player还有一个PlayMusic方法
static void Main(string[] args)
{
Computer com = new Computer();
//1接口插上Mp3
com[1]=new Mp3();
com.Read(1);
com.Write(1);
Mp3 mp3 = com[1] as Mp3;
mp3.PlayMusic();
Console.ReadKey();
}
}
//电脑
class Computer
{
//代表电脑上的四个接口
MoveEquipment [] Usbs=new MoveEquipment[4];
//面向抽象编程(根据抽象标准生产,不管具体行为)
public MoveEquipment this[int index]
{
get {return Usbs[index];}
set {Usbs[index]=value;}
}
public void Read(int index)
{
if (Usbs[index] != null)
{
Usbs[index].Write();
}
}
public void Write(int index)
{
if (Usbs[index] != null)
{
Usbs[index].Read();
}
}
}
//移动设备抽象类
abstract class MoveEquipment
{
public abstract void Write();
public abstract void Read();
}
//U盘
class UPan : MoveEquipment
{
public override void Read()
{
Console.WriteLine("U盘读取");
}
public override void Write()
{
Console.WriteLine("U盘写入");
}
}
//Mp3
class Mp3:MoveEquipment
{
public override void Read()
{
Console.WriteLine("Mp3读取");
}
public override void Write()
{
Console.WriteLine("Mp3写入");
}
public void PlayMusic()
{
Console.WriteLine("Mp3播放音乐");
}
}
//硬盘
class HardPan:MoveEquipment
{
public override void Read()
{
Console.WriteLine("硬盘读取");
}
public override void Write()
{
Console.WriteLine("硬盘写入");
}
}
10接口
接口定义了一种能力和规范,其内成员可以是除了字段之外的其他数据类型,接口中的方法只有签名部分,无修饰符方法体等。接口之间可以多继承,同样一个类也可以继承自多个接口,实现接口的类需要实现接口中的所有成员,实现的方法的访问修饰符为public,可以被抽象实现,显示实现接口默认访问级别问private,不能加修饰符。
与抽象类想比,接口通常用于为某几个互补相干的类提供某种通用的规范和能力,抽象类用于描述有继承关系的某些类。