类声明的全貌
- 类声明与创建类实例不同
- C#类声明的位置:
- 在namespace内,常用
- 在类内,即成员类
- 在namespace外,也是在全局namespace内
- 在C#和java中声明即定义,在C++中声明与定义分开了(也可以合并在一起)
类的访问控制,类修饰符
- public:跨程序集访问
- internal:默认的访问级别,只能在当前程序集访问
- static:静态类
- abstract:修饰后表示为抽象类,不能被实例化,只能作为基类或多态使用
- sealed:修饰后表示为封闭类,不能被继承
- new:使用与嵌套类,指定类隐藏同名的继承成员
- protected:在类作为成员类时,才能用此修饰符
- private:在类作为成员类时,才能用此修饰符
如何写出优质的代码——阅读、参考优质的开源项目代码
类的继承
- 类的继承(基类、基接口):是子类在完整接收父类的前提下,对父类进行横向,或纵向扩展
- 横向扩展:对类成员个数扩充
- 纵向扩展:对类成员版本更新或重写override
- 类在功能上的扩展(extend)
- 只能有一个基类,但可以实现多个基接口
- 基类与派生类是成对出现
- 父类与子类是成对出现
- 类访问级别对继承的影响
- sealed类不能被继承
- 是一个(is a)概念:派生类的实例也是基类的一个实例(反之不成立),可以用父类类型的变量来引用子类类型的实例
- 当一个类被sealed修饰时,表示此类为封闭类,无法被继承,无法派生一个新类
- 子类的访问级别不能超过父类的访问级别(internal,public)
- “is a”示例: ```csharp using System;
namespace HelloOOP { class Program { static void Main(string[] args) { Car car = new Car(); Console.WriteLine(car is Vehicle);//输出:True
//is a更深入的概念:多态
Vehicle v = new Car();
Object o1 = new Vehicle();
Object o2 = new Car();
}
}
class Vehicle
{
//Vehicle是基类
}
class Car : Vehicle
{
//Car是派生类
}
}
<a name="t7oZl"></a>
# 成员的继承
- 派生类对继承成员的访问
- 派生类对基类成员的访问
- 构造器的不可继承性:需用base来写子类构造器
- 示例1:子类对父类成员的全盘继承,类成员只能增加不能减少(因此在新增类成员时必须警慎,否则会造成污染)
```csharp
using System;
namespace HelloOOP
{
class Program
{
static void Main(string[] args)
{
RaceCar raceCar = new RaceCar();
raceCar.Owner = "RaceCar";//raceCar有Owner属性,因此子类对父类的成员会全盘继承
Car car = new Car();//创建继承链上的对象时,先调用父类构造器,后调用子类构造器
Console.WriteLine(car.Owner);
}
}
class Vehicle
{
public Vehicle()
{
this.Owner = "N/A";
}
public string Owner { get; set; }
}
class Car : Vehicle
{
//当子类构造器被调用时,父类构造器一定会被先于调用
public Car()
{
this.Owner = "Car Owner";
}
public void ShowOwner()
{
Console.WriteLine(this.Owner);
Console.WriteLine(base.Owner);//base表示父类对象,且只能向上访问一层
//由于子类继承父类的Owner,所以this.Owner与base.Owner在内存中指向的是同一个值,因此this和base可以省略
//如果父类的Owner访问级别是private,则此处不能用base.Owner,因为Owner虽然被继承了,但无权限访问
}
}
class RaceCar : Car
{
}
}
- 示例2:父类的实例构造器不能被子类继承。如果父类有一个带参数的构造器,由于不能被继承,子类构造器必须进行改写,如下: ```csharp using System;
namespace HelloOOP { class Program { static void Main(string[] args) { Car car = new Car();//方式一调用 Console.WriteLine(car.Owner);
Car car1= new Car("Tim");//方式二调用
Console.WriteLine(car1.Owner);
}
}
class Vehicle
{
public Vehicle(string owner)
{
this.Owner = owner;
}
public string Owner { get; set; }
}
class Car : Vehicle
{
//父类构造器带参数,子类构造器改写方式一:
//表示子类在调用父类构造器时,将string类型的"N/A"传给父类构造器
public Car() : base("N/A")
{
this.Owner = "Car Owner";
}
//方式二
public Car(string owner):base(owner)
{
}
public void ShowOwner()
{
Console.WriteLine(Owner);
}
}
}
<a name="QeGdq"></a>
# 成员的访问级别
- 是以类的访问级别为上限的,如果一个类库中的类访问级别是internal(即类名前无修饰),那么即使类成员被public修饰,在其他程序集也无法访问此类的成员。
- 在A程序集引用B程序集的名称空间时,B程序集中必须要有类为public,写using时才能看到B程序集的名称空间
- public:表示类成员是可以跨程序集被访问
- internal:表示类成员只能在当前程序集被访问
- private:
- 默认访问级别,表示类成员只能在当前类中被访问
- 父类的private成员在子类中可以被继承,但子类没权限访问
- protected:
- 只能被继承链上的对象/类访问,无法被其他类访问,且为跨程序集的
- 设计时,应考虑此成员仅内部继承链上使用,其他类封装不显示,多用于方法中(重写)
- protected可以和internal一起使用,为"或"关系,既可以被其派生类访问,又可以被当前程序集中所有其他类访问
- 示例:创建MyLib类库,如下:
```csharp
using System;
namespace MyLib
{
public class Vehicle
{
//_rpm字段,子类可以访问,而对其他类进行封闭,所以设计时用protected修饰
protected int _rpm;
//_fuel字段,只能在当前类中进行访问
private int _fuel;
//Refuel方法设计时为公开的,每个人都可以进行加油操作
public void Refuel()
{
_fuel = 100;
}
//Burn方法由于其燃油功能为交通工具内部方法,只能被继承链上的类访问,不能被其他类使用
protected void Burn(int fuel)
{
_fuel -= fuel;
}
//增速方法,设计为公开的方法
public void Accelerate()
{
Burn(1);
_rpm += 1000;
}
//只读属性Speed
public int Speed
{
get{return _rpm/100;}
}
}
public class Car : Vehicle
{
//Car子类新增TurboAccelerate方法,并继承父类Vehicle所有成员
public void TurboAccelerate()
{
Burn(2);
_rpm += 3000;//父类_rpm为protected时可以访问,如果为private则无法访问
}
}
}
在主程序中编写如下代码:
using System;
using MyLib;
namespace HelloAccess
{
class Program
{
static void Main(string[] args)
{
Car car = new Car();
car.Refuel();//先加满油
car.TurboAccelerate();//在进行一次加速
Console.WriteLine(car.Speed);
}
}
class Bus : Vehicle
{
public void SlowAccelerate()
{
//被protected修饰的成员,可以被跨程序集的子类访问
Burn(1);
_rpm += 500;
}
}
}
面向对象的实现风格
- Class-based:基于类的面向对象系统,在C++、Java、C#中运用
- Prototype-based:基于原型的面向对象系统,在javascript中运用