封装:怎么样来隐藏一个对象的内部实现的?
继承:怎么样来促进代码重用的?
多态:怎么让你用同样的方式来处理相关对象的?
OOP的代码重用:
Is-a关系,就是传统继承(classical inheritance)
Has-a 关系,就是包含/委托模型,就是一个类可以定义另一个类的成员变量并向外界公开它的部分或全部功能。例如:给一辆汽车建模,可能想表达一辆车has-a(有一个)收音机的概念。让Radio继承Car类或者反之都是不合逻辑的。实际上,你有两个类一起合作,包含类创建并公开了被包含类的功能:
public class Radio
{
public void Power(bool turnOn)
{
Console.WriteLine("Radio on:{0}", turnOn);
}
}
public class Car
{
//汽车has-a收音机
private Radio myRadio=new Radio();
public void TurnOnRadio(bool onOff)
{
//到内部对象的委托
myRadio.Power(onOff);
}
}
在这里,包含类型(car)负责创建被包含对象(radio)。如果car想让radio的行为通过car的实例访问,它必须扩展自己的公共接口,添加被包含类型的Radio的一些功能。注意:对象用户不知道car类在使用内部的radio对象。
class Program
{
static void Main(string[] args)
{
//调用在内部转发到Radio
Car viper = new Car();
viper.TurnOnRadio(true);
}
}
OOP的多态:这个原则允许基类为所有的派生类定义一个成员集合[正式的术语为多态接口(ploymorphic interface)]。一个类类型的多态接口由任意个虚拟(virtual)或抽象(abstruct)成员组成。简单说,虚拟成员可能被继承类改变(更正式的说法为重写,override),而抽象方法必须被派生类重写。当派生类重写由基类定义的成员时,其实就重定义了响应相同请求的方法。例如:因为hexagon和cricle继承自一个公共的父类(shape),shape类型的一个数组可以包含任何的派生类。此外,因为shape对所有的继承类型定义了一个多态接口(在这里是Draw()方法),我们可以认为数组中的每一个成员都有这个功能。如下面的Main()方法,它指示一个Shape的继承类型的数组使用Draw()方法绘制自己:
static void Main(string[] args)
{
//创建一个从Shape继承的项的数组
Shape[] myShapes=new Shape[3];
myShapes[0]=new Hexagon();
myShapes[1]=new Cricle();
myShapes[2]=new Hexagon();
//迭代整个数组并绘制每一项
foreach(Shape s in myShapes)
s.Draw();
}
类类型之间类型转换的第一个规则就是,当两个类之间是is-a关系时,将派生类型存储在基类引用中总是安全的。这称之为隐式转换(implicit cast),因为它就是基于继承的规则。这就产生了一个很强大的编程结构。例如,如果有名为TheMachine的类,它支持下面的静态方法:
Plublic class TheMachine
{
Public static void FireThisPersion(Employee e)
{
//从数据库中删除
}
}
因为有is-a关系,实际可以从Employee类派生来的元素直接传递到这个方法中:
TheMachine.FireThisPerson(mooUnit); //mooUnit声明为Employee
TheMachine.FireThisPerson(jill); //jill声明为SalesPerson
但是不能将System.Object当作直接继承的Employee,因为Object "is-not-a" Employee。
Object frank = new Manager("Frank Zappa",9,4000);
TheMachine.FireThisPerson(frank); // 这个是错误的!
但是可以通过一个强制转换让编译器通过验证:
//从通用的System.Object转换成一个强类型的Manager。
Manager mar=(Manager)frank;
Console.WriteLine("Frank's options:{0}",mgr.NumbOpts);
如果不想声明一个目标类型的具体变量的话,可以精简为下面的代码:
Console.WriteLine("Frank's options:{0}",((Manager)frank).NumbOpts);
至于将System.Object引用传递到FireThisPerson()方法中的问题,就可以这么解决了:
//显示转换System.Object为一个Employee
TheMachine.FireThisPerson((Emplayee)frank);
因为静态的TheMachine.FireThisPerson()方法被设计为可接受任何可能从Employee继承来的类型,这个方法怎么确定传递的是哪一种继承类型呢?于此相关的是,既然传入的参数是Employee类型的,怎么样才能访问SalesPerson和Manager的指定成员呢?C#提供三种方法判断基类引用是否实际上引用的是继承类型的方式:显示类型转换,is关键字,as关键字。Is关键字很有用,它返回一个代表基类引用是否与给定继承类型兼容的布尔值。考虑下面已修的FireThisPerson()方法:
public class TheMachine
{
public static void FireThisPerson(Employee e)
{
if (e is SalesPerson)
{
Console.WriteLine("Lost a sales person named {0}", e.GetFullName());
Console.WriteLine("{0} made {1} sales...", e.GetFullName(), (SalesPerson)e.NumbSales);
}
if (e is Manager)
{
Console.WriteLine("Lost a suit named {0}", e.GetFullName());
Console.WriteLine("{0} had {1} stock options...", e.GetFullName(), (Manager)e.NumbOpts);
}
}
}
也可以使用as关键字获取一个对继承类型的引用(如果类型间不兼容,引用被设置为null):
SalesPerson p= e as SalesPerson;
If(p!=null)
console.WriteLine("# of sales: {0}",p.NumbSales);