我們在這裡會進行結構的成員宣告與定義,其中最常用的就是
欄位
、屬性
、建構函式
、方法
的宣告。
在這個結構筆記中,我們定義了底下的結構
在這個結構中,我們定義了一個完整屬性 X 的宣告,你可以使用程式碼片段
propfull
來快速產生這個定義程式碼,這裡表示了屬性 X 的實際儲存內容,將會儲存在欄位 _X 內,當您要進行讀取/寫入動作的時候,是透過了 get存取子和set存取子 這兩個方法來間接存取欄位 _X。
另外,我們使用程式碼片段
prop
這個程式碼片段,宣告了一個自動建置的屬性 Y,在這樣的用法中,我們並不需要額外的定義一個欄位 _Y ,因為,編譯器會幫我們自動產生這些程式碼。使用了自動建置的屬性,大幅簡化了我們在屬性上的程式碼撰寫,也讓我們的程式碼更加的清爽,最重要的是,這兩種做法的最終效果,都是一樣的。
最後,我們定義了一個欄位 Z。
不過,與類別有點不同的地方,在要建立結構執行個體的時候,可以不需要使用 new 運算子與結構預設函式,就可以直接存取結構內的各個欄位,並且,在結構內,我們不能夠定義預設建構函式,但是,可以定義其他有參數的建構函式。
在這個結構範例程式碼中,您會看到我們並沒已宣告任何建構函式,此時,你可以直接使用預設建構函式來建立這個結構執行個體;然而,您會在底下的程式碼中看到有個有兩個參數的建構函式,若您將這個有參數的建構函式解除註解之後,您就可以在程式碼中,使用兩個引述與這個建構函式,來建立出這個結構的執行個體;不過,就在這個時候,您也無法繼續在使用預設建構函式來產生執行個體了。
最後,我們學習如何在結構中產生這個結構應該提供的行為,也就是方法。
Length()
這個方法我們使用了 private
存取修飾詞,宣告這個函式僅能夠在結構內來呼叫,我們會在結構內的 Print()
來呼叫,而 Print()
方法的存取修飾詞為 public
,這表示,任何程式碼皆可以透過執行個體來執行這個方法。
最後,結構與類別有著很大的不同特性,您需要確實瞭解這兩個類型的相同與相異點,才能夠使用他們設計出符合您需要的型別與物件。
public struct MyPoint
{
#region 座標點 (使用 region 來做到程式碼區隔)
// 可以使用程式碼片段 propfull
private double _X;
public double X
{
get { return _X; }
set { _X = value; }
}
// 可以使用程式碼片段 prop
public double Y { get; set; }
public double Z;
#endregion
// 底下的預設建構式,會產生甚麼錯誤訊息呢?
//public MyPoint()
//{
// _X = 0;
// Y = 0;
// Z = 0;
//}
// 底下的程式碼為何會出錯
//public MyPoint(double x, double y)
//{
// X = x;
// Y = y;
//}
/// <summary>
/// 計算座標點長度
/// (使用 XML 註解,提升程式碼閱讀性)
/// 這個方法僅限類別內可以使用
/// </summary>
/// <returns></returns>
private double Length()
{
return Math.Sqrt(X * X + Y * Y);
}
/// <summary>
/// 顯示座標詳細資訊
/// 任何程式碼都可以存取這個方法
/// </summary>
/// <returns></returns>
public string Print()
{
return $"({X}, {Y}) 的長度是 {Length()}";
}
}
使用類別產生物件,進而存取類別內的資料與行為
這個程式碼
MyPoint fooObject4;
表示宣告一個物件變數 fooObject4
,由於這個物件的型別是結構,因此,這個物件變數已經可以讓您直接存取該結構物件的內容。這樣的特性將會是與類別所不盡相同的。
另外,當執行底下程式碼
fooObject4 = fooObject1;
這表示了
fooObject4
會持有與 fooObject1
相同的物件值,不過,這個時候,這兩個物件變數,此時分別指向了不同的兩個物件。這樣的特性,也是與類別的用法不太一樣的。
在最後面,我們宣告了
MyPoint fooObject6;
fooObject6 這個物件,接著,我們存取了其欄位 Z,您可以從 Visual Studio 中看到,這樣的行為是被允許的。
不過,當要存取屬性 X或者屬性 Y,卻會得到了
錯誤 CS0165 使用未指派的區域變數 'fooObject6'
這樣的錯誤訊息,這是因為,在這個時候,欄位的值,尚未被初始化,所以,會產生錯誤;這樣的用法也是和類別不太相同的。
最後,當我們宣告一個結構物件,有 new 運算子與建構函式或者
default
,則這樣就可以得到一個已經完全初始化的結構物件,例如這樣的做法 MyPoint fooObject7 = new MyPoint() { X = 5, Y = 4 };
。經過這樣獲得的結構物件,我們就可以存取該結構物件內的欄位與屬性了。 class Program
{
static void Main(string[] args)
{
// 宣告一個 MyPoint 結構的物件變數,並且該變數持有這個所有成員的值
MyPoint fooObject1 = new MyPoint() { X = 5, Y = 4 };
// 宣告一個 MyPoint 類別的物件變數,並且指向新產生的 MyPoint 物件
//MyPoint fooObject2 = new MyPoint(5, 4);
// 宣告一個 MyPoint 類別的物件變數,並且指向新產生的 MyPoint 物件
var fooObject3 = new MyPoint() { X = 5, Y = 4 };
// 宣告一個 MyPoint 類別的物件變數,不需要使用任何 new 運算子與建構函式
MyPoint fooObject4;
// 將物件變數的參考值指向另外一個 MyPoint 物件
fooObject4 = fooObject1;
// 宣告一個 MyPoint 類別的物件變數,使用 Default
MyPoint fooObject5 = default(MyPoint);
// 在這裡,你可以呼叫 Length() 方法嗎?
Console.WriteLine($"X:{fooObject1.X}, Y:{fooObject1.Y} / 更多詳細資訊 {fooObject1.Print()}");
Console.WriteLine($"X:{fooObject3.X}, Y:{fooObject3.Y} / 更多詳細資訊 {fooObject3.Print()}");
Console.WriteLine($"X:{fooObject4.X}, Y:{fooObject4.Y} / 更多詳細資訊 {fooObject4.Print()}");
Console.WriteLine("Press any key for continuing...");
Console.ReadKey();
Console.WriteLine("修改物件的內容");
fooObject1.X = 7.0;
fooObject3.X = 8.0;
fooObject4.X = 9.0;
Console.WriteLine($"X:{fooObject1.X}, Y:{fooObject1.Y} / 更多詳細資訊 {fooObject1.Print()}");
Console.WriteLine($"X:{fooObject3.X}, Y:{fooObject3.Y} / 更多詳細資訊 {fooObject3.Print()}");
Console.WriteLine($"X:{fooObject4.X}, Y:{fooObject4.Y} / 更多詳細資訊 {fooObject4.Print()}");
Console.WriteLine("Press any key for continuing...");
Console.ReadKey();
fooObject5.X = 10;
fooObject5.Y = 20;
Console.WriteLine($"X:{fooObject5.X}, Y:{fooObject5.Y} / 更多詳細資訊 {fooObject5.Print()}");
Console.WriteLine("Press any key for continuing...");
Console.ReadKey();
MyPoint fooObject7 = new MyPoint() { X = 5, Y = 4 };
fooObject7.Z = 16;
fooObject7.X = 16;
fooObject7.Y = 20;
MyPoint fooObject6;
// 這裡為什麼可以設定成員 欄位的值
fooObject6.Z = 16;
// 這裡為什麼無法設定成員 屬性的值
fooObject6.X = 16;
// 若把上一行註解,這裡為什麼無法設定成員 屬性的值
fooObject6.Y = 20;
Console.WriteLine("Press any key for continuing...");
Console.ReadKey();
}
}
了解更多關於 [結構]
了解更多關於 [C# 程式設計手冊]
structClass
關鍵字來進行定義,這個型別屬於實值型別,有別於類別的參考型別(您需要能夠區分這兩種實值型別 Value Type
與參考型別 Reference Type
的差異)。