面向对象的三个基本特征:
封装——把客观事物封装成类,并将类内部的实现隐藏,以保证数据的完整性。
继承——通过继承可以复用父类的代码。
多态——允许将子对象赋值给父对象的一种能力。
一、封装
定义:把内部的数据隐藏起来,不让对象实例直接对其操作。
C#提供了属性机制来对类内部的状态进行操作。在C#中封装可以通过public、private、protected和internal等关键字来体现
class Person
{
private string _name;
private int _age;
public string Name
{
get{return _name;}
set{_name = value;}
}
public int Age
{
get{return _age;}
set
{
//在属性定义中,可以根据系统的业务逻辑添加逻辑代码
if( value < 0 || value > 120 )
{
throw(new ArgumentOutOfRangeException("AgeIntProery",value,"年龄必须哎0~120之间"));
}
_age = value;
}
}
}
封装特性是一种保护状态数据完整性的方法,一般更多地定义私有数据字段,通过C#提供的属性机制来对这些私有字段数据进行间接的操作,并且可以在属性的定义中加入更多的逻辑判断。
二、继承
1.C#中的继承
定义:一个类可以继承另外一个已有的类(密封类除外),被继承的类称为基类(或父类),继承的类称为派生类(或子类),子类将获得基类除构造函数和析构函数以外的所有成员。此外,静态类是密封的,也不能被继承。
注意:C#不支持多继承,但可以继承多个接口
public class Animal
{
private int _age;
public int Age
{
get
{
return _age;
}
set
{
//在属性定义中,可以根据系统的业务逻辑添加逻辑代码
if( value < 0 || value > 10 )
{
throw ( new ArgumentOutOfRangeException( "AgeIntProery", value, "年龄必须哎0~120之间" ) );
}
_age = value;
}
}
}
//马(子类)
public class Horse : Animal
{
}
//羊(子类)
public class Sheep : Animal
{
}
注意:子类不能直接访问父类的私有成员,子类可以通过调用公有或保护方法来间接地对私有成员进行访问。
2.密封类
C#使用sealed关键字来定义密封类,密封类不能被另外一个类继承。
3.子类的初始化顺序
(1).初始化类的实例字段
(2).调用基类的构造函数,如果没有指明基类,则调用System.Object的构造函数
(3).调用子类的构造函数
public class Animal
{
//2.调用基类的构造函数
public Animal( )
{
Console.WriteLine( "Animal construction" );
}
private int _age;
public int Age
{
get
{
return _age;
}
set
{
//在属性定义中,可以根据系统的业务逻辑添加逻辑代码
if( value < 0 || value > 10 )
{
throw ( new ArgumentOutOfRangeException( "AgeIntProery", value, "年龄必须哎0~120之间" ) );
}
_age = value;
}
}
}
//马(子类)
public class Horse : Animal
{
//3.调用子类的构造函数
public Horse()
{
Console.WriteLine( "Horse construction" );
}
//1.初始化类的实例字段
private double weight = 200;
public void Print( )
{
Console.WriteLine( "Horse Weight = " + weight );
}
}
class Program
{
static void Main( string[ ] args )
{
Horse horse = new Horse( );
horse.Print( );
Console.Read( );
}
}
运行结果:
三、多态
定义:子类需要覆写父类中的方法来实现子类特有的行为。这样相同类型的对象调用相同的方法可以表现出不同的现象。
1.使用virtual和override关键字实现方法重写
只有基类成员声明为virtual或abstract时,才能被派生类重写。如果子类想改变虚方法的实现行为,必须使用override关键字
public class Animal
{
public virtual void Voice( )
{
Console.WriteLine( "Animal voice!" );
}
private int _age;
public int Age
{
get
{
return _age;
}
set
{
//在属性定义中,可以根据系统的业务逻辑添加逻辑代码
if( value < 0 || value > 10 )
{
throw ( new ArgumentOutOfRangeException( "AgeIntProery", value, "年龄必须哎0~120之间" ) );
}
_age = value;
}
}
}
//马(子类)
public class Horse : Animal
{
public override void Voice( )
{
//调用基类的方法
base.Voice( );
Console.WriteLine( "horse voice: si~~~" );
}
}
//羊(子类)
public class Sheep : Animal
{
public override void Voice( )
{
//调用基类的方法
base.Voice( );
Console.WriteLine( "horse voice: mie~~~" );
}
}
class Program
{
static void Main( string[ ] args )
{
Horse horse = new Horse( );
horse.Voice( );
Sheep sheep = new Sheep( );
sheep.Voice( );
Console.Read( );
}
}
运行结果:
上述示例还存在一个问题:Animal类不是具体的类型,不能构建一个具体的实例,我们应该将其使用abstract关键字来防止在代码中直接创建这样类的实例
public abstract class Animal
{
...
}
2.阻止派生类重写虚函数
前面介绍到,用sealed关键字可以防止一个类被其他类继承。同样,也可以使用sealed关键字来阻止派生类重写虚函数。
//马(子类)
public class Horse : Animal
{
public sealed override void Voice( )
{
//调用基类的方法
base.Voice( );
Console.WriteLine( "horse voice: si~~~" );
}
}
这样,如果尝试在Horse的派生类中重写Voice方法的话,会收到“无法对密封成员进行复写”的错误信息。
3.使用新成员隐藏基类成员
如果想在派生类中定义与基类成员同名的成员,则可以使用new关键字把基类成员隐藏起来。如果不使用new关键字,则会产生警告信息。
public class Animal
{
public void Print( )
{
Console.WriteLine( "Animal print" );
}
}
//马(子类)
public class Horse : Animal
{
public void Print( )
{
Console.WriteLine( "Horse print" );
}
}
编译会出现警告:
如果确实需要添加某个方法,但是该方法又与基类的方法同名,那么可以使用new关键字把基类成员隐藏。
//马(子类)
public class Horse : Animal
{
public new void Print( )
{
Console.WriteLine( "Horse print" );
}
}
这样基类成员Print就会被隐藏,但是如果仍然想访问基类的成员,则可使用强制类型转换,把子类强制转换成基类类型,从而访问隐藏的基类成员。
class Program
{
static void Main( string[ ] args )
{
Horse horse = new Horse( );
//调用Horse中的Print方法
horse.Print( );
//调用基类的Print方法
( (Animal)horse ).Print( );
Console.Read( );
}
}
运行结果: