類別或結構定義就像藍圖,指定類型可以執行的動作。 物件基本上是已根據藍圖配置和設定的記憶體區塊。 程式可能會建立相同類別的許多物件。 物件也稱為實例,而且可以儲存在具名變數或陣列或集合中。 用戶端程式代碼是使用這些變數來呼叫 方法並存取 物件的公用屬性的程序代碼。 在 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
的記憶體都已配置在線程堆疊上。 該記憶體會連同宣告的類型或方法一起回收。 這是在指派時複製結構的原因之一。 相較之下,當物件的所有參考都已脫離範圍時,Common Language Runtime 會自動回收為類別實例配置的記憶體(垃圾收集)。 您無法確定性地終結類別物件,就像在 C++ 中一樣。 如需 .NET 中垃圾收集的詳細資訊,請參閱 垃圾收集。
備註
受控堆積上的記憶體配置和解除分配在 Common Language Runtime 中高度優化。 在大部分情況下,在堆積上配置類別實例的效能成本與在堆疊上配置結構實例的效能成本沒有顯著差異。
對象識別與值相等
當您比較兩個物件是否相等時,您必須先區分兩個變數是否代表記憶體中的相同物件,或其中一或多個字段的值是否相等。 如果您想要比較數值,您必須考慮物件是實值型別(結構體)或參考型別的實例(類別、委託、陣列)。
若要判斷兩個類別實例是否參考記憶體中的相同位置(這表示它們具有相同的 身分識別),請使用靜態 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)中所述的指導方針。
相關區段
如需詳細資訊,請參閱: