【C#与.NET程序设计】(4)- C#类及OOP

来源:互联网 发布:国密 java 编辑:程序博客网 时间:2024/06/06 17:59

类的串联构造函数

考虑如下构造函数重载

// 构造函数的重载class Motorcycle{    public int driverIntensity;    public string driverName;    // 默认构造函数    public Motorcycle(){}    // 冗余的构造函数    public Motorcycle(int intensity)    {        if(intensity > 10)            intensity = 10;        driverIntensity = intensity;    }    public Motorcycle(int intensity, string name)    {        if(intensity > 10)            intensity = 10;        driverIntensity = intensity;        driverName = name;    }}

其中 intensity 的验证代码是冗余的:

  • 一种方法是创建一个函数专门负责 intensity 的验证
  • 另一种更好的方法是使用串联构造函数链
class Motorcycle{    public int driverIntensity;    public string driverName;    // 构造函数链    public Motorcycle(){}    public Motorcycle(int intensity):this(intensity, "")    {}    public Motorcycle(string name):this(0, name)    {}    // 主构造函数    public Motorcycle(int intensity, string name)    {        if(intensity > 10)            intensity = 10;        driverIntensity = intensity;        driverName = name;    }}

调用流程:
类比构造函数参数列表,首先进入主构造函数(包含所有参数),然后才进构造函数链中特定构造函数

此外,.NET4.0 平台还支持可选参数的方法创建构造函数

// 可选参数的单个构造函数// 仅 .NET4 平台支持class Motorcycle{    public int driverIntensity;    public string driverName;    public Motorcycle(int intensity = 0, string name = “”)    {        if(intensity > 10)            intensity = 10;        driverIntensity = intensity;        driverName = name;    }}

类的static关键字

类(或结构)可以通过 static 定义静态成员

  • 类内的静态函数和静态变量不是通过类实例调用
  • 静态成员只能操作静态数据或调用类的静态方法
  • 类内的静态变量只会初始化一次,类的不同实例共享同一个静态变量
// 静态方法不通过类实例调用// 如Console类的 WriteLine() 方法Console.WriteLine("i am static");  // 正确Console c = new Console();         // 错误c.WriteLine("its wrong");

静态成员的初始化

  • 直接在变量声明时赋值
  • 使用静态构造函数赋值
// 不管创建多少个对象,确保静态数据只被分配一次// 声明时初始化class SavingAccount{    public double currBalance;    public static double currInterestRate = 0.04;}// 使用静态构造函数// 针对静态变量需要在运行时获取(比如数据库或外部文件)的情况class SavingAccount{    public double currBalance;    public static double currInterestRate;    static SavingAccount()    {        currInterestRate = 0.04;     }}

静态构造函数的注意点

  • 一个类只可以定义一个静态构造函数(不允许重载)
  • 静态构造函数不允许访问修饰符,不接受任何参数
  • 无论创建了多少对象,静态构造函数只执行一次

静态类

// 静态类static class TimeUtilClass{    public static void PrintTime()    {...}    public static void PrintDate()    {...}}
  • 静态类只能包含静态成员
  • 不能使用 new 创建静态类,直接用就好(TimeUtilClass.PrintDate();)

OOP

OOP的三个核心:封装、继承、多态

  • 封装:隐藏一个对象的内部实现并保护数据完整性
  • 继承:如何促进代码重用
    通过继承(”is-a”关系),子类可以继承基类核心的功能,并扩展基类的行为
    “has-a”关系:一个类定义另一个类的成员变量
  • 多态:如何用同样的方式处理相关对象
    基类定义抽象(abstract)/虚拟(virtual)方法,派生类重写该方法以实现多态

访问修饰符

修饰符 可用到的地方 作用 public 类型或类型成员 无限制,可以从对象及任何派生类访问;公共类可以冲外部程序集访问 private 类型成员或嵌套类型 只能由定义它们的类(或结构)进行访问 protected 类型成员或嵌套类型 可以由定义它们的类及其子类使用,但外部类无法访问 internal 类型或类型成员 只能在当前程序集内访问 protected internal 类型成员或者嵌套类型 在定义它们的程序集、类以及派生类中可用
  • 默认类型成员是 private 的;类型是 internal 的
  • class 只能用 public 或 internal 修饰

使用 .NET 属性进行封装

  • 产品级代码类成员尽量为 private ,外部通过类方法读取和修改成员
  • .NET提倡使用属性来强制数据封装
// 利用 属性 实现强制封装class Employee{    //字段数据    private string empName;    //属性    public string Name    {        get{return empName;}        set        {            if(value.Length > 15)                Console.WriteLine("Invalid name");            else                empName = value        }    }}// 直接调用Name来实现读写Employee emp = new Employee();emp.Name = "Marv";Console.WriteLine("Employee is named: {0}", emp.Name);
  • Name 这个属性直接跟empName挂钩(两者类型一致都是 string)
  • 通过实现 set 和 get 方法完成 private 变量的读写
  • value 总是表示调用者设置的值,value类型与属性本身类型一致
  • 构造函数中通过使用类属性来初始化变量
// 使用 Name 属性更新构造函数public Employee(string name){    Name = name;}
  • 不要再定义 set_Name() 和 get_Name() 方法,因为编译器会将 set 和 get 解释为 set_ 和 get_,会导致混淆
  • set 和 get 默认是 public的,可以用修饰符改变其可见性,也可以设为 只读/只写
// set/get 可见性// 仅当前类及其派生类成员可以赋值public string SocialSecurityNumber{    get{return empSSN;}    protected set{empSSN = value;}}// 只读// 不实现 set 就可以了public string SocialSecurityNumber{    get{return empSSN;}}// 只写// 不实现 get 就可以了public string SocialSecurityNumber{    set{empSSN = value;}}
  • 自动属性
    对于包含多个变量的类,挨个实现 get 和 set 是很麻烦的
    如果没有其他额外操作(比如赋值检查),仅仅是读/写,可以使用自动属性
// 自动属性// 自动属性不允许只读/只写设定class Car{    public string PetName{get; set;}  // get 和 set 必须写全,无法设定只读/只写    // 等价于    private string carName;    public string PetName    {        get {return carName;}        set {carName = value;}    }}

对象初始化器

在实际中,类构造函数往往无法初始化所有数据
对象初始化器可以只用少量代码实现数据初始化

// 对象初始化器class Rectangle{    private Point topLeft = new Point();    private Point bottomRight = new Point();    public Point TopLeft    {        get {return topLeft;}        set {topLeft = value;}    }    public Point BottomRight    {        get {return bottomRight;}        set {bottomRight = value;}    }}// 传统的初始化方法Rectangle r = new Rectangle();Point p1 = new Point();p1.X = 10;p1.Y = 10;r.TopLeft = p1;Point p2 = new Point();p2.X = 20;p2.Y = 20;r.BottomRight = p2;// 对象初始化器技术// 调用默认构造函数Rectangle r = new Rectangle{TopLeft = new Point{X = 10, Y=10},                            BottomRight = new Point{X=200, Y=200}};

常量数据和只读字段

// 常量数据// 定义后不能修改class mathClass{    public const double PI = 3.14;}// 常量数据默认是 静态,所以调用方式为mathClass.PI;//*******************************// 只读字段// 与常量数据的不同是:1.可以在构造函数中赋值(运行时决定值)//                     2. 默认不是静态class mathClass{    public readonly double PI = 3.14;    // 或者    public readonly double PI;    public mathClass()    {        PI = 3.14;    }}

分部类型

一般来说一个类一个.cs文件,对于比较复杂的类,可以用分部类型 partial 将类的实现切分到多个 .cs 文件

// 分部类型// 编译器会将两个文件的内容合并到一起// employee_1.cspartial class Employee{}// employee_2.cspartial class Employee{}

参考

【1】C#与.NET 4高级程序设计(第5版)