类或结构定义就像一个蓝图,指定类型可以执行什么功能。 对象基本上是已根据蓝图分配和配置的内存块。 程序可能会创建同一类的许多对象。 对象也称为实例,并且可以存储在命名变量或数组或集合中。 客户端代码是使用这些变量调用方法和访问对象公共属性的代码。 在面向对象的语言(如 C#)中,典型的程序由多个对象动态交互组成。
注释
静态类型的行为方式与此处所述的不同。 有关详细信息,请参阅 静态类和静态类成员。
结构实例与类实例
由于类是引用类型,因此类对象的变量保留对托管堆上对象的地址的引用。 如果将同一类型的第二个变量分配给第一个变量,则这两个变量都引用该地址处的对象。 本文稍后将更详细地讨论这一点。
使用运算符创建new
类的实例。 在以下示例中,Person
是该类型的类型和person1
person2
实例或对象。
using System;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
// Other properties, methods, events...
}
class Program
{
static void Main()
{
Person person1 = new Person("Leopold", 6);
Console.WriteLine($"person1 Name = {person1.Name} Age = {person1.Age}");
// Declare new person, assign person1 to it.
Person person2 = person1;
// Change the name of person2, and person1 also changes.
person2.Name = "Molly";
person2.Age = 16;
Console.WriteLine($"person2 Name = {person2.Name} Age = {person2.Age}");
Console.WriteLine($"person1 Name = {person1.Name} Age = {person1.Age}");
}
}
/*
Output:
person1 Name = Leopold Age = 6
person2 Name = Molly Age = 16
person1 Name = Molly Age = 16
*/
由于结构是值类型,因此结构对象的变量保存整个对象的副本。 还可以使用 new
运算符创建结构实例,但这不是必需的,如以下示例所示:
using System;
namespace Example
{
public struct Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
public class Application
{
static void Main()
{
// Create struct instance and initialize by using "new".
// Memory is allocated on thread stack.
Person p1 = new Person("Alex", 9);
Console.WriteLine($"p1 Name = {p1.Name} Age = {p1.Age}");
// Create new struct object. Note that struct can be initialized
// without using "new".
Person p2 = p1;
// Assign values to p2 members.
p2.Name = "Spencer";
p2.Age = 7;
Console.WriteLine($"p2 Name = {p2.Name} Age = {p2.Age}");
// p1 values remain unchanged because p2 is copy.
Console.WriteLine($"p1 Name = {p1.Name} Age = {p1.Age}");
}
}
/*
Output:
p1 Name = Alex Age = 9
p2 Name = Spencer Age = 7
p1 Name = Alex Age = 9
*/
}
p1
和 p2
的内存都分配在线程堆栈上。 该内存随声明它的类型或方法一起回收。 这是结构在赋值时被复制的原因之一。 相比之下,当对对象的所有引用都超出了范围时,公共语言运行时会自动回收为类实例分配的内存(垃圾回收)。 无法确定性地销毁类对象,就像在C++中一样。 有关 .NET 中的垃圾回收的详细信息,请参阅 垃圾回收。
注释
公共语言运行时中高度优化了托管堆上内存的分配和释放。 在大多数情况下,在堆上分配类实例的性能成本与在堆栈上分配结构实例的性能成本没有显著差异。
对象标识与值相等性
比较两个对象是否相等时,必须首先区分是想知道这两个变量是否表示内存中的同一对象,还是两个变量的一个或多个字段的值是等效的。 如果要比较值,则必须考虑对象是值类型(结构)的实例还是引用类型(类、委托、数组)。
若要确定两个类实例是否引用内存中的相同位置(这意味着它们具有相同 的标识),请使用静态 Object.Equals 方法。 (System.Object 是所有值类型和引用类型的隐式基类,包括用户定义的结构和类。
若要确定两个结构实例中的实例字段是否具有相同的值,请使用 ValueType.Equals 该方法。 由于所有结构都隐式继承自 System.ValueType,因此直接在对象上调用该方法,如以下示例所示:
// Person is defined in the previous example. //public struct Person //{ // public string Name; // public int Age; // public Person(string name, int age) // { // Name = name; // Age = age; // } //} Person p1 = new Person("Wallace", 75); Person p2 = new Person("", 42); p2.Name = "Wallace"; p2.Age = 75; if (p2.Equals(p1)) Console.WriteLine("p2 and p1 have the same values."); // Output: p2 and p1 have the same values.
System.ValueType 的
Equals
实现在某些情况下使用装箱和反射。 有关如何提供特定于类型的有效相等算法的信息,请参阅 如何为类型定义值相等性。 记录是使用值语义实现相等的引用类型。若要确定两个类实例中字段的值是否相等,可以使用 Equals 该方法还是 == 运算符。 但是,只有类通过重写或重载提供关于那种类型对象的“相等”含义的自定义时,才能使用它们。 该类还可以实现 IEquatable<T> 接口或 IEqualityComparer<T> 接口。 这两个接口都提供可用于测试值相等性的方法。 设计好替代
Equals
的类后,请务必遵循如何为类型定义值相等性和 Object.Equals(Object) 中介绍的准则。
相关部分
更多信息,请参阅: