物件 - 建立型別的執行個體
類別或結構定義就像是指定型別可以做什麼的藍圖。 物件基本上是根據藍圖配置和設定的記憶體區塊。 程式可建立許多同類別的物件。 物件也稱為執行個體,可儲存在具名變數或陣列或集合中。 用戶端程式碼是使用這些變數呼叫方法,並存取物件公用屬性的程式碼。 在 C# 之類的物件導向語言中,一般程式包含多個動態互動的物件。
注意
靜態型別的行為會和此處描述的不同。 如需詳細資訊,請參閱靜態類別和靜態類別成員。
結構執行個體與類別執行個體
因為類別是參考型別,所以類別物件的變數會將參考保留在 Managed 堆積上的物件位址中。 如果同型別的第二個變數指派給第一個,則這兩個變數都會參考位於該位址的物件。 本文中稍後會詳細討論這一點。
使用 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 = {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);
}
}
/*
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 = {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);
}
}
/*
Output:
p1 Name = Alex Age = 9
p2 Name = Spencer Age = 7
p1 Name = Alex Age = 9
*/
}
p1
和 p2
的記憶體都配置在執行緒堆疊上。 該記憶體會和其宣告所在的型別或方法一起回收。 這是在指派時複製結構的原因之一。 相較之下,當物件的所有參考都超出範圍時,通用語言執行平台會自動回收為類別執行個體配置的記憶體 (記憶體回收)。 您不可能像在 C++ 中一樣決定性終結類別物件。 如需 .NET 記憶體回收的詳細資訊,請參閱記憶體回收。
注意
通用語言執行平台中,已高度最佳化 Managed 堆積上的記憶體配置和解除配置。 在大部分情況下,在堆積上配置類別執行個體與在堆疊上配置結構執行個體,效能成本沒有任何重大差異。
物件識別與實值相等
當您比較兩個物件是否相等時,您必須先區別是否想要知道這兩個變數在記憶體中是否代表相同的物件,或者其欄位的一或多個值是否相等。 如果您打算比較值,就必須考慮物件是實值型別 (結構) 還是參考型別 (類別、委派、陣列) 的執行個體。
若要判斷兩個類別執行個體在記憶體中是否參考相同的位置 (表示它們具有相同的「身分識別」),請使用靜態 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.
Equals
的 System.ValueType 實作在部分案例中會使用 boxing 和反映。 如需如何提供特定於您的型別的有效相等演算法相關資訊,請參閱<如何定義型別的實質相等>。 記錄使用值語意進行相等的參考型別。若要判斷兩個類別執行個體中的欄位值是否相等,您或許可以使用 Equals 方法或 == 運算子。 但請只有當類別覆寫或多載它們,以提供「相等」表示的型別物件的自訂定義時,才使用它們。 此類別可能也會實作 IEquatable<T> 介面或 IEqualityComparer<T> 介面。 這兩個介面都會提供可用以測試值相等的方法。 若要設計自己的類別以覆寫
Equals
,請務必遵循如何定義型別的實值相等和Object.Equals(Object) 所述的指導方針。
相關章節
其他資訊: