C#中的类与对象定义及实例化
C# 中的类与对象定义及实例化
类的定义基础
在 C# 编程中,类是一种用户自定义的数据类型,它封装了数据(字段)和操作这些数据的方法。可以把类看作是一种蓝图,用于创建对象。对象是类的具体实例,每个对象都拥有类中定义的属性和方法的副本。
类的定义语法如下:
[访问修饰符] class [类名]
{
// 字段
[字段修饰符] [数据类型] [字段名];
// 方法
[方法修饰符] [返回类型] [方法名]([参数列表])
{
// 方法体
}
}
其中,访问修饰符
用于控制类、字段和方法的访问级别,常见的有 public
(公共的,任何代码都能访问)、private
(私有的,只有类内部能访问)、protected
(受保护的,只有类及其子类能访问)等。类名
要遵循 C# 的命名规范,通常采用 Pascal 命名法,即每个单词的首字母大写。
例如,我们定义一个简单的 Person
类:
public class Person
{
// 字段
private string name;
private int age;
// 方法
public void SetName(string newName)
{
name = newName;
}
public void SetAge(int newAge)
{
age = newAge;
}
public void DisplayInfo()
{
Console.WriteLine($"Name: {name}, Age: {age}");
}
}
在上述 Person
类中,定义了两个私有字段 name
和 age
,分别用于存储人的姓名和年龄。同时定义了三个公共方法 SetName
、SetAge
和 DisplayInfo
,用于设置姓名和年龄以及显示个人信息。由于字段是私有的,外部代码不能直接访问和修改它们,必须通过公共方法来操作,这体现了面向对象编程中的数据封装原则。
构造函数
构造函数是类中的一种特殊方法,用于在创建对象时初始化对象的状态。构造函数的名称与类名相同,并且没有返回类型(包括 void
也没有)。
- 默认构造函数
如果在类中没有显式定义任何构造函数,C# 编译器会自动为该类生成一个默认构造函数。默认构造函数没有参数,并且会将对象的所有字段初始化为它们的默认值。例如,数值类型初始化为 0,引用类型初始化为
null
。
public class Point
{
private int x;
private int y;
// 编译器会自动生成默认构造函数
}
在上述 Point
类中,虽然没有显式定义构造函数,但编译器会生成如下默认构造函数:
public Point()
{
x = 0;
y = 0;
}
- 自定义构造函数 当需要在创建对象时进行特定的初始化操作时,就需要定义自定义构造函数。
public class Rectangle
{
private int width;
private int height;
// 带参数的构造函数
public Rectangle(int w, int h)
{
width = w;
height = h;
}
public int CalculateArea()
{
return width * height;
}
}
在上述 Rectangle
类中,定义了一个带两个参数的构造函数 Rectangle(int w, int h)
,用于在创建 Rectangle
对象时初始化 width
和 height
字段。这样在创建 Rectangle
对象时,就可以直接传入宽度和高度的值。
Rectangle rect = new Rectangle(5, 10);
int area = rect.CalculateArea();
Console.WriteLine($"The area of the rectangle is: {area}");
类的继承
继承是面向对象编程的重要特性之一,它允许一个类(子类)继承另一个类(父类)的成员(字段、方法等)。通过继承,子类可以复用父类的代码,并且可以添加自己特有的成员或重写父类的方法。
在 C# 中,使用 :
符号来表示继承关系。例如:
public class Animal
{
protected string name;
public Animal(string n)
{
name = n;
}
public virtual void MakeSound()
{
Console.WriteLine("The animal makes a sound.");
}
}
public class Dog : Animal
{
public Dog(string n) : base(n)
{
}
public override void MakeSound()
{
Console.WriteLine("The dog barks.");
}
}
在上述代码中,Dog
类继承自 Animal
类。Dog
类通过 : base(n)
调用了父类 Animal
的构造函数来初始化 name
字段。Animal
类中的 MakeSound
方法被声明为 virtual
,表示该方法可以在子类中被重写。Dog
类使用 override
关键字重写了 MakeSound
方法,提供了自己特有的实现。
Animal animal = new Animal("Generic Animal");
animal.MakeSound();
Dog dog = new Dog("Buddy");
dog.MakeSound();
Animal dogAsAnimal = new Dog("Max");
dogAsAnimal.MakeSound();
上述代码中,首先创建了一个 Animal
对象并调用其 MakeSound
方法,输出 “The animal makes a sound.”。然后创建了一个 Dog
对象并调用其 MakeSound
方法,输出 “The dog barks.”。最后,将 Dog
对象赋值给 Animal
类型的变量 dogAsAnimal
,并调用 MakeSound
方法,由于实际对象是 Dog
,所以仍然会调用 Dog
类中重写的 MakeSound
方法,输出 “The dog barks.”。这体现了 C# 中的多态性,即同一个方法在不同的对象上表现出不同的行为。
静态类和静态成员
- 静态类
静态类是一种特殊的类,它不能被实例化,并且只能包含静态成员(静态字段、静态方法等)。静态类主要用于包含一些与特定功能相关的工具方法或常量。定义静态类使用
static
关键字。
public static class MathUtils
{
public static int Add(int a, int b)
{
return a + b;
}
public static int Multiply(int a, int b)
{
return a * b;
}
}
在上述 MathUtils
静态类中,定义了两个静态方法 Add
和 Multiply
,用于执行加法和乘法运算。由于是静态类,不能通过实例化对象来调用这些方法,而是直接通过类名来调用。
int sum = MathUtils.Add(3, 5);
int product = MathUtils.Multiply(4, 6);
- 静态成员 在普通类中,也可以定义静态字段和静态方法。静态字段属于类本身,而不是类的实例,所有对象共享同一个静态字段。静态方法只能访问静态成员,不能访问实例成员(字段和方法)。
public class Counter
{
private static int count = 0;
public Counter()
{
count++;
}
public static int GetCount()
{
return count;
}
}
在上述 Counter
类中,定义了一个静态字段 count
,用于统计创建的 Counter
对象的数量。每次创建 Counter
对象时,构造函数会将 count
加 1。GetCount
方法是一个静态方法,用于获取当前的对象数量。
Counter counter1 = new Counter();
Counter counter2 = new Counter();
int totalCount = Counter.GetCount();
Console.WriteLine($"Total number of counters: {totalCount}");
对象的实例化
- 使用
new
关键字 在 C# 中,最常见的对象实例化方式是使用new
关键字。new
关键字不仅会为对象分配内存空间,还会调用对象的构造函数进行初始化。
Person person = new Person();
person.SetName("John");
person.SetAge(30);
person.DisplayInfo();
上述代码中,首先使用 new
关键字创建了一个 Person
对象,然后通过调用对象的公共方法设置姓名和年龄,并显示个人信息。
- 对象初始化器 对象初始化器是一种便捷的语法,允许在创建对象时同时初始化对象的属性。
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
Book book = new Book
{
Title = "C# Programming",
Author = "Some Author"
};
在上述代码中,定义了 Book
类,并使用对象初始化器在创建 Book
对象时直接设置 Title
和 Author
属性的值。
- 集合初始化器
对于包含对象的集合,如
List<T>
,可以使用集合初始化器来快速创建并初始化集合中的对象。
using System.Collections.Generic;
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
List<Product> products = new List<Product>
{
new Product { Name = "Laptop", Price = 1000m },
new Product { Name = "Mouse", Price = 50m }
};
上述代码中,使用集合初始化器创建了一个 List<Product>
集合,并在集合中添加了两个 Product
对象,同时初始化了它们的属性。
类与对象的内存管理
- 栈和堆 在 C# 中,对象的内存分配涉及到栈和堆两个内存区域。栈主要用于存储局部变量和方法调用的上下文信息,它的特点是存取速度快,但空间有限。堆则用于存储对象实例,它的空间相对较大,但存取速度相对较慢。 当定义一个引用类型的变量时,变量本身存储在栈上,而变量所引用的对象实例存储在堆上。例如:
Person person; // 变量 person 存储在栈上,此时未指向任何对象
person = new Person(); // 使用 new 关键字在堆上创建 Person 对象,并将对象的引用赋给栈上的 person 变量
- 垃圾回收 C# 采用自动垃圾回收机制来管理对象的内存释放。当一个对象不再被任何变量引用时,它就成为了垃圾回收的候选对象。垃圾回收器会在适当的时候扫描堆内存,回收这些不再使用的对象所占用的内存空间,从而避免内存泄漏。
{
Person person = new Person();
// 对 person 进行操作
} // person 离开作用域,不再被引用,成为垃圾回收候选对象
在上述代码中,当 person
变量离开其作用域后,它所引用的 Person
对象不再被任何变量引用,垃圾回收器会在后续的某个时刻回收该对象所占用的内存。
嵌套类
嵌套类是指在一个类的内部定义另一个类。嵌套类可以访问外部类的所有成员,包括私有成员。嵌套类主要用于将相关的类组织在一起,提高代码的封装性和可读性。
public class OuterClass
{
private int outerField = 10;
public class InnerClass
{
public void DisplayOuterField()
{
OuterClass outer = new OuterClass();
Console.WriteLine($"Outer field value: {outer.outerField}");
}
}
}
在上述代码中,InnerClass
是 OuterClass
的嵌套类。InnerClass
的 DisplayOuterField
方法可以访问 OuterClass
的私有字段 outerField
。使用嵌套类时,需要通过外部类来创建内部类的对象。
OuterClass.InnerClass inner = new OuterClass.InnerClass();
inner.DisplayOuterField();
抽象类和抽象方法
- 抽象类
抽象类是一种不能被实例化的类,它主要用于作为其他类的基类,为子类提供一个通用的框架。抽象类可以包含抽象方法和非抽象方法。定义抽象类使用
abstract
关键字。
public abstract class Shape
{
public abstract double CalculateArea();
public void DisplayShapeType()
{
Console.WriteLine("This is a shape.");
}
}
在上述 Shape
抽象类中,定义了一个抽象方法 CalculateArea
,该方法没有方法体,子类必须重写该方法。同时还定义了一个非抽象方法 DisplayShapeType
。
- 抽象方法 抽象方法是在抽象类中声明的没有实现的方法,它只有方法签名,没有方法体。子类继承抽象类后,必须重写抽象类中的抽象方法,除非子类本身也是抽象类。
public class Circle : Shape
{
private double radius;
public Circle(double r)
{
radius = r;
}
public override double CalculateArea()
{
return Math.PI * radius * radius;
}
}
在上述 Circle
类中,继承自 Shape
抽象类,并实现了 CalculateArea
抽象方法,提供了计算圆面积的具体实现。
密封类和密封方法
- 密封类
密封类使用
sealed
关键字修饰,它不能被其他类继承。密封类通常用于防止意外的继承,确保类的行为和实现不会被修改。
public sealed class FinalClass
{
// 类的成员
}
在上述代码中,FinalClass
是一个密封类,任何试图继承它的操作都会导致编译错误。
- 密封方法
密封方法是在子类中使用
sealed
关键字修饰的重写方法,它阻止子类的子类进一步重写该方法。
public class BaseClass
{
public virtual void SomeMethod()
{
Console.WriteLine("Base class method.");
}
}
public class DerivedClass : BaseClass
{
public sealed override void SomeMethod()
{
Console.WriteLine("Derived class method.");
}
}
public class FurtherDerivedClass : DerivedClass
{
// 以下代码会导致编译错误,因为 SomeMethod 在 DerivedClass 中已被密封
// public override void SomeMethod()
// {
// Console.WriteLine("Further derived class method.");
// }
}
在上述代码中,DerivedClass
重写了 BaseClass
的 SomeMethod
方法,并将其声明为密封方法。因此,FurtherDerivedClass
不能再重写 SomeMethod
方法。
通过以上对 C# 中类与对象的定义、实例化以及相关特性的详细介绍,希望能帮助开发者更深入地理解和运用 C# 进行面向对象编程,编写出更加健壮、可维护的代码。在实际编程中,合理运用这些知识,可以优化代码结构,提高代码的复用性和可扩展性。例如,在开发大型项目时,通过类的继承和多态,可以有效地组织和管理复杂的业务逻辑;利用静态类和静态成员,可以实现一些通用的工具方法,提高代码的执行效率和可维护性。同时,对对象实例化和内存管理的深入理解,有助于避免内存泄漏等问题,提升程序的性能。在编写嵌套类、抽象类和密封类时,要根据具体的业务需求进行合理设计,以达到最佳的编程效果。总之,熟练掌握 C# 中类与对象的相关知识,是成为一名优秀 C# 开发者的重要基础。