類或結構定義的作用類似於藍圖,指定該類型可以進行哪些操作。 從本質上說,對象是按照此藍圖分配和配置的內存塊。 程序可以創建同一個類的多個對象。 對象也稱爲實例,可以存儲在命名變量中,也可以存儲在數組或集合中。 使用這些變量來調用對象方法及訪問對象公共屬性的代碼稱爲客戶端代碼。 在 C# 等面向對象的語言中,典型的程序由動態交互的多個對象組成。
結構實例. 選件類實例
由於類是引用類型,因此類對象的變量引用該對象在託管堆上的地址。 如果將同一類型的第二個對象分配給第一個對象,則兩個變量都引用該地址的對象。 這一點將在本主題後面部分進行更詳細的討論。
類的實例是使用 new 運算符創建的。 在下面的示例中,Person 爲類型,person1 和 person 2 爲該類型的實例(即對象)。
C#
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 = {0} Age = {1}", person1.Name, 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 = {0} Age = {1}", person2.Name, person2.Age);
Console.WriteLine("person1 Name = {0} Age = {1}", person1.Name, person1.Age);
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/*
Output:
person1 Name = Leopold Age = 6
person2 Name = Molly Age = 16
person1 Name = Molly Age = 16
*/
由於結構是值類型,因此結構對象的變量具有整個對象的副本。 結構的實例也可以使用 new 運算符來創建,但這不是必需的,如下面的示例所示:
C#
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 = {0} Age = {1}", p1.Name, 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 = {0} Age = {1}", p2.Name, p2.Age);
// p1 values remain unchanged because p2 is copy.
Console.WriteLine("p1 Name = {0} Age = {1}", p1.Name, p1.Age);
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/*
Output:
p1 Name = Alex Age = 9
p2 Name = Spencer Age = 7
p1 Name = Alex Age = 9
*/
p1 和 p2 的內存在線程堆棧上進行分配。 該內存隨聲明它的類型或方法一起回收。 這就是在賦值時複製結構的一個原因。 相比之下,當對類實例對象的所有引用都超出範圍時,爲該類實例分配的內存將由公共語言運行時自動回收(垃圾回收)。 無法像在 C++ 中那樣明確地銷燬類對象。
Note:公共語言運行時中高度優化了託管堆上內存的分配和釋放。 在大多數情況下,在堆上分配類實例與在堆棧上分配結構實例在性能開銷上沒有顯著的差別。
對象標識與. 值相等性
在比較兩個對象是否相等時,首先必須明確您是想知道兩個變量是否表示內存中的同一對象,還是想知道這兩個對象的一個或多個字段的值是否相等。 如果您要對值進行比較,則必須考慮這兩個對象是值類型(結構)的實例,還是引用類型(類、委託、數組)的實例。
- 若要確定兩個類實例是否引用內存中的同一位置(意味着它們具有相同的標識),可使用靜態 Equals 方法。 (System.Object
是所有值類型和引用類型的隱式基類,其中包括用戶定義的結構和類。) - 若要確定兩個結構實例中的實例字段是否具有相同的值,可使用 ValueType.Equals 方法。 由於所有結構都隱式繼承自
System.ValueType,因此可以直接在對象上調用該方法,如下面的示例所示:
C#
// 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;
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.
Equals 的 System.ValueType 實現使用反射,因爲它必須能夠確定任何結構中有哪些字段。 在創建您自己的結構時,重寫 Equals 方法可以提供針對您的類型的高效求等算法。
- 要確定兩個類實例中字段的值是否相等,您可以使用 Equals 方法或 == 運算符。
但是,只有類通過已重寫或重載提供關於那種類型對象的相等含義的自定義時,才能使用它們。 類也可以實現 IEquatable 接口或IEqualityComparer 接口。 這兩個接口都提供可用於測試值相等性的方法。 設計好重寫 Equals 的類後,請務必遵循如何:爲類型定義值相等性和 Object.Equals(Object) 中介紹的準則。