c#编程
引言
.NET 框架由多种编程语言组成,这些语言都遵循面向对象编程 (OOP) 的开发方法。OOP 的三大核心原则是:
- 继承:能够继承并扩展现有功能。
- 封装:能够对外部隐藏内部实现,仅暴露必要的接口。
- 多态:能够动态分配对象类型,但可预测对象可执行的操作。
在 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
)。它定义了一些所有对象都可用的方法,例如:
GetHashCode()
:返回对象的唯一哈希值。GetType()
:检索对象的信息,如方法名和类名。ToString()
:将对象转换为文本表示,通常用于输出。
因为所有类默认继承自 System.Object
,所有类都会自动拥有这些方法。
对象基础
默认是引用类型
所有对象默认是引用类型。要支持值类型,对象需要从 System.ValueType
抽象类继承,而不是 System.Object
。
构造函数 (Constructors)
构造函数是初始化对象的特殊方法,用于在对象创建时设置其初始状态。
构造函数类型
- 默认构造函数:无参数。
- 重载构造函数:带有参数。
- 复制构造函数:复制另一个对象的值。
- 静态构造函数:仅用于初始化静态成员。
构造函数特点
- 构造函数的名称与类名相同。
- 通常声明为
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
构造函数重载
- 多个构造函数允许根据需求提供多种初始化方式。
- 如果需要默认构造函数,必须显式定义。
总结
- OOP 的核心原则:继承、封装、多态。
- 引用类型与值类型:
- 引用类型是指向值的指针。
- 值类型直接存储值。
- 方法与属性:
- 属性用于访问和设置对象的状态。
- 方法用于执行操作。
System.Object
:- 所有对象默认继承该类,并拥有基本方法。
- 构造函数:
- 初始化对象状态。
- 支持默认构造、重载构造和静态构造。
通过这些功能,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()
是该类的析构函数,用于释放资源或执行清理操作。
总结
-
默认构造函数:
- 当没有定义其他构造函数时,编译器会生成一个默认构造函数。
-
重载构造函数:
- 允许开发者为对象提供多种初始化方式。
- 每个构造函数可以接受不同类型和数量的参数。
-
调用其他构造函数:
- 使用
this
关键字在一个构造函数中调用另一个构造函数。
- 使用
-
调用基类构造函数:
- 使用
base
关键字调用基类的构造函数。
- 使用
-
析构函数:
- 使用
~
定义析构函数。 - 垃圾回收器决定析构函数的调用时间,但建议使用
Dispose()
方法进行显式资源管理。
- 使用
这些构造函数和析构函数为 C# 对象提供了灵活的生命周期管理。