引言

.NET 框架由多种编程语言组成,这些语言都遵循面向对象编程 (OOP) 的开发方法。OOP 的三大核心原则是:

  1. 继承:能够继承并扩展现有功能。
  2. 封装:能够对外部隐藏内部实现,仅暴露必要的接口。
  3. 多态:能够动态分配对象类型,但可预测对象可执行的操作。

在 OOP 中,对象可以类比为现实世界中的物体。每个对象都有其外观、测量方式以及与外界交互的方式。在设计 OOP 语言时,初衷是模拟人类的思维方式,以简化编码体验。

例如,考虑一个椅子对象,其属性包括尺寸、重量、颜色和材料。在 .NET 中,这些被称为属性(Properties),用来定义对象的状态。然而,暴露对象数据有两种方式:字段(Fields)和属性(Properties),推荐使用属性而非字段。


引用类型与值类型

引用类型

引用类型类似于指向值的指针。例如,一张写有地址的纸条可以指向某个房屋(对象)。地址(引用)存储在内存中,可以访问该地址以找到原始对象,而无需复制整个对象。

值类型

值类型直接存储值,非常适合存储小型信息,如数字或日期。

引用类型和值类型的处理方式不同,稍后会详细讲解。


对象的交互:方法

除了查询对象的值外,我们还需要通过某些方式与对象交互,例如文件对象的 Read() 方法。方法允许我们对对象执行操作。

示例:矩形对象

  • 属性Length(长度)、Width(宽度)
  • 方法
    • Area():计算面积(Length * Width
    • Perimeter():计算周长(2 * Length + 2 * Width

方法不同于属性,因为方法通常需要转换数据以实现结果。例如,椅子的 Sit() 方法不会返回任何值,只是执行动作。


System.Object

为了支持 OOP 的继承规则,.NET 定义了一个所有对象都派生的基类:System.Object(或简写为 object)。它定义了一些所有对象都可用的方法,例如:

  1. GetHashCode():返回对象的唯一哈希值。
  2. GetType():检索对象的信息,如方法名和类名。
  3. ToString():将对象转换为文本表示,通常用于输出。

因为所有类默认继承自 System.Object,所有类都会自动拥有这些方法。


对象基础

默认是引用类型

所有对象默认是引用类型。要支持值类型,对象需要从 System.ValueType 抽象类继承,而不是 System.Object


构造函数 (Constructors)

构造函数是初始化对象的特殊方法,用于在对象创建时设置其初始状态。

构造函数类型

  1. 默认构造函数:无参数。
  2. 重载构造函数:带有参数。
  3. 复制构造函数:复制另一个对象的值。
  4. 静态构造函数:仅用于初始化静态成员。

构造函数特点

  • 构造函数的名称与类名相同。
  • 通常声明为 public,以便在类外部创建对象。
  • 构造函数没有返回类型(甚至没有 void)。
  • 如果未定义构造函数,C# 会提供一个默认构造函数,将成员初始化为零。
  • 一旦定义了自定义构造函数,默认构造函数将不再可用。

构造函数的作用

  • 初始化对象的成员变量。
  • 通过 new 关键字调用构造函数。
  • 允许定义多个构造函数(构造函数重载),每个构造函数具有不同的参数签名。

示例:

public class Rectangle
{
    public int Length { get; set; }
    public int Width { get; set; }

    // 默认构造函数
    public Rectangle()
    {
        Length = 0;
        Width = 0;
    }

    // 带参数的构造函数
    public Rectangle(int length, int width)
    {
        Length = length;
        Width = width;
    }
}

public class Program
{
    public static void Main()
    {
        // 使用默认构造函数
        Rectangle rect1 = new Rectangle();
        Console.WriteLine($"Length: {rect1.Length}, Width: {rect1.Width}");

        // 使用带参数的构造函数
        Rectangle rect2 = new Rectangle(10, 5);
        Console.WriteLine($"Length: {rect2.Length}, Width: {rect2.Width}");
    }
}

输出:

Length: 0, Width: 0
Length: 10, Width: 5

构造函数重载

  • 多个构造函数允许根据需求提供多种初始化方式。
  • 如果需要默认构造函数,必须显式定义。

总结

  1. OOP 的核心原则:继承、封装、多态。
  2. 引用类型与值类型
    • 引用类型是指向值的指针。
    • 值类型直接存储值。
  3. 方法与属性
    • 属性用于访问和设置对象的状态。
    • 方法用于执行操作。
  4. System.Object
    • 所有对象默认继承该类,并拥有基本方法。
  5. 构造函数
    • 初始化对象状态。
    • 支持默认构造、重载构造和静态构造。

通过这些功能,C# 提供了强大的面向对象开发能力,简化了复杂系统的设计和实现。

复制构造函数(Copy Constructor)

复制构造函数通过从另一个对象中复制变量来创建对象。当创建所需类型的对象并将要复制的对象作为参数传递时,调用复制构造函数。

在以下示例中,我们将一个 Rectangle 对象传递给 Rectangle 的构造函数,使新对象拥有与旧对象相同的值:

using System;

namespace CopyConstructor
{
    class Rectangle
    {
        public int length;
        public int breadth;

        public Rectangle(int x, int y)  // 构造函数
        {
            length = x;
            breadth = y;
        }

        public Rectangle(Rectangle r)  // 复制构造函数
        {
            length = r.length;
            breadth = r.breadth;
        }

        public void display()
        {
            Console.WriteLine("Length = " + length);
            Console.WriteLine("Breadth = " + breadth);
        }
    }   // 类 Rectangle 结束

    class Program
    {
        public static void Main()
        {
            Rectangle r1 = new Rectangle(5, 10);
            Console.WriteLine("第一个对象的值:");
            r1.display();

            Rectangle r2 = new Rectangle(r1);
            Console.WriteLine("第二个对象的值:");
            r2.display();

            Console.ReadLine();
        }
    }
}

静态构造函数(Static Constructor)

静态构造函数在运行时第一次访问类时调用。静态变量始终可访问,因此运行时必须在其首次访问时进行初始化。以下示例中,当通过调试器逐步执行时,会发现静态构造函数 static MyClass() 仅在访问 MyClass.Number 变量时才被调用。

C# 支持两种类型的构造函数:静态构造函数和实例构造函数。

  • 实例构造函数在每次创建该类对象时都会被调用。
  • 静态构造函数仅调用一次。它在创建任何类对象之前调用,通常用于初始化类的静态数据成员。

静态构造函数使用关键字 static 声明。该构造函数不能有参数或访问修饰符。此外,一个类只能有一个静态构造函数。示例如下:

using System;

namespace StaticConstructors
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            int j = 0;
            Console.WriteLine("Static Number = " + MyClass.Number);
        }
    }

    class MyClass
    {
        private static int _number;
        public static int Number { get { return _number; } }
        
        static MyClass()
        {
            Random r = new Random();
            _number = r.Next();
        }
    }
}

默认构造函数 (Default Constructor)

 

简介

 

默认构造函数不接受任何参数。如果类中未定义其他构造函数,编译器会隐式定义一个默认构造函数。

 

示例代码:

 

由开发者创建的类:

 
class MyClass
{
}
 

编译器自动生成的代码:

 
class MyClass : System.Object
{
     public MyClass() : base()
     {
     }
}
 

 

重载构造函数 (Overloaded Constructors)

 

简介

 

通过传递参数,构造函数可以对对象进行自定义初始化。这允许开发者以不同形式初始化对象。

 

示例代码:

 
class MyClass
{
    private int _number;
    public int Number { get { return _number; } }
    
    // 默认构造函数
    public MyClass()
    {
        Random randomNumber = new Random();
        _number = randomNumber.Next();
    }

    // 带参数的构造函数
    public MyClass(int seed)
    {
        Random randomNumber = new Random(seed);
        _number = randomNumber.Next();
    }
}
 

在上述代码中:

 
  • 默认构造函数生成一个随机数。
  • 带参数的构造函数根据 seed 参数生成一个随机数。
 

 

调用其他构造函数 (Calling Other Constructors)

 

简介

 

为了减少代码重复,可以在一个构造函数中调用另一个构造函数(默认或重载)。这通过 this 关键字实现。

 

示例代码:

 
class MyClass
{
    private int _number;
    public int Number { get { return _number; } }
    
    // 默认构造函数,调用另一个构造函数
    public MyClass() : this(DateTime.Now.Millisecond)
    {
    }

    // 带参数的构造函数
    public MyClass(int seed)
    {
        Random r = new Random(seed);
        _number = r.Next();
    }
}
 

在这里,MyClass() 调用了 MyClass(int seed) 构造函数,并传递了当前时间的毫秒值作为参数。

 

 

调用基类构造函数 (Calling Base Class Constructors)

 

简介

 

除了调用当前实例的构造函数,还可以调用基类的构造函数。这通过 base 关键字实现。

 

示例代码:

 
class MyException : Exception
{
    private int _number;
    public int Number { get { return _number; } }
    
    public MyException(int errorNumber, string message, Exception innerException)
        : base(message, innerException) // 调用基类的构造函数
    {
        _number = errorNumber;
    }
}
 

在此示例中,MyException 调用了基类 Exception 的构造函数,并将消息和内部异常传递给基类。

 

 

析构函数 (Destructors)

 

简介

 

对象在被垃圾回收器清理时可以执行一些清理操作,析构函数就是为此设计的。析构函数的名称与类名相同,但前面加上 ~ 符号。

 

注意:

 
  • 垃圾回收器只有在显式调用或需要回收内存时才会运行,因此析构函数可能在很长时间后才被调用。
  • 如果需要及时释放资源,建议使用 IDisposable 接口的 Dispose() 方法。
 

示例代码:

 
class MyException : Exception
{
    private int _number;
    public int Number { get { return _number; } }
    
    public MyException(int errorNumber, string message, Exception innerException)
        : base(message, innerException)
    {
        _number = errorNumber;
    }

    // 析构函数
    ~MyException()
    {
        // 清理逻辑
    }
}
 

在此示例中,~MyException() 是该类的析构函数,用于释放资源或执行清理操作。

 

 

总结

 
  1. 默认构造函数

    • 当没有定义其他构造函数时,编译器会生成一个默认构造函数。
  2. 重载构造函数

    • 允许开发者为对象提供多种初始化方式。
    • 每个构造函数可以接受不同类型和数量的参数。
  3. 调用其他构造函数

    • 使用 this 关键字在一个构造函数中调用另一个构造函数。
  4. 调用基类构造函数

    • 使用 base 关键字调用基类的构造函数。
  5. 析构函数

    • 使用 ~ 定义析构函数。
    • 垃圾回收器决定析构函数的调用时间,但建议使用 Dispose() 方法进行显式资源管理。
 

这些构造函数和析构函数为 C# 对象提供了灵活的生命周期管理。


Last modified: Saturday, 11 January 2025, 10:57 PM