C#結構體:從C/C++時代遷移過來的經典。結構體與類相似,也有大量不同之處 。結構體對做為C#開發者來說有很多價值。一般不需要用到結構體,但是有些方面結構體比類做得好。
結構體是什么?
結構體是class對象的小姐妹。初相見,他們之間區別不大。他們都包含不同類型的字段的集合,如下
1 public struct Flower 2 { 3 public string name; 4 protected int beautyRating; 5 private bool isVascular; 6 }
可以試一下結構體。做為一種簡單的數據結構,可以將結構體視為一種字段包。可能你會用結構體做為數據傳輸對象,或者可能你想要在其它基於字段值的地方運行一些邏輯。
也可以用結構體封裝行為,如下:
1 public struct FlowerWithBehavior 2 { 3 string name; 4 int beautyRating; 5 bool isVascular; 6 7 public void makeBeautiful() 8 { 9 beautyRating = 10; 10 } 11 }
到目前為止,結構體看起來像類,只是關鍵詞不同。話雖這樣說,但是結構在一些重要方式還是有別於類。
什么使結構與眾不同?
最重要的不同之處是:結構體是值類型,類是引用類型。類和結構體相似,如下:
1 public struct FlowerStruct 2 { 3 string name; 4 int beautyRating; 5 bool isVascular; 6 7 public FlowerStruct(string name, int beautyRating, bool isVascular) 8 { 9 this.name = name; 10 this.beautyRating = beautyRating; 11 this.isVascular = isVascular; 12 } 13 } 14 15 public class FlowerClass 16 { 17 public string name; 18 public int beautyRating; 19 public bool isVascular; 20 21 public FlowerClass(string name, int beautyRating, bool isVascular) 22 { 23 this.name = name; 24 this.beautyRating = beautyRating; 25 this.isVascular = isVascular; 26 } 27 }
盡管看起來相似,但是類和結構體有不同之處:
1 public void Test_Struct_Vs_Class_Equality() 2 { 3 FlowerStruct struct1 = new FlowerStruct("Daisy", 3, true); 4 FlowerStruct struct2 = new FlowerStruct("Daisy", 3, true); 5 6 FlowerClass class1 = new FlowerClass("Daisy", 3, true); 7 FlowerClass class2 = new FlowerClass("Daisy", 3, true); 8 9 Assert.Equal(struct1, struct2); 10 Assert.NotEqual(class1, class2); 11 }

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Drawing; 6 using System.Windows.Forms; 7 using Microsoft.VisualStudio.TestTools.UITesting; 8 9 namespace Struct_And_Class 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 FlowerStruct struct1 = new FlowerStruct("Daisy",3,true); 16 FlowerStruct struct2 = new FlowerStruct("Daisy", 3, true); 17 18 FlowerClass class1 = new FlowerClass("Daisy",3,true); 19 FlowerClass class2 = new FlowerClass("Daisy", 3, true); 20 21 bool Result1 = Equals(struct1,struct2); 22 bool Result2 = Equals(class1,class2); 23 24 Console.WriteLine("結構體對比結果是{0}",Result1); 25 Console.WriteLine("類的對比結果是{0}",Result2); 26 } 27 } 28 29 public struct FlowerStruct 30 { 31 string name; 32 int beautyRating; 33 bool isVascular; 34 35 public FlowerStruct(string name, int beautyRating,bool isVascular) 36 { 37 this.name = name; 38 this.beautyRating = beautyRating; 39 this.isVascular = isVascular; 40 } 41 } 42 43 public class FlowerClass 44 { 45 string name; 46 int beautyRating; 47 bool isVascular; 48 49 public FlowerClass(string name, int beautyRating, bool isVascular) 50 { 51 this.name = name; 52 this.beautyRating = beautyRating; 53 this.isVascular = isVascular; 54 } 55 } 56 }
如上面完整代碼所呈現的,對於結構體來說,只要是內部是相等相同的,結果就是相等的;而類不相等,與字段沒有關系。
這或許是結構體與類之間最大的不同之處。用struct,可以實現拆箱操作,可以創建值類型對象。
同樣,結構體永遠不是null
1 public void Test_Struct_Not_Null( 2 { 3 Flower flowerStruct = null; //<-- Compile error. 4 }
空引用錯誤是軟件中的Bug原因之一。
當然,可以欺騙一下,使它們成為可空類型(nullable)。
注意構造函數的挑戰
冷靜一下,結構體不是完美的。如果你想要結構體做為不可改變的值對象,如下面示例所示,需要一個無參數的構造函數。這意味着你的值對偶處在一種無效狀態。
1 public struct StrictFlower 2 { 3 public string name; 4 public int beautyRating; 5 6 public StrictFlower(string name, int beautyRating) 7 { 8 this.name = name; 9 if (name == "daisy") 10 this.beautyRating = beautyRating; 11 else 12 this.beautyRating = 5; 13 } 14 } 15 16 [Fact] 17 public void Struct_Always_Has_Parameterless_Constructor() 18 { 19 StrictFlower flower = new StrictFlower(); 20 flower.name = "daisy"; 21 int notFive = 3; 22 flower.beautyRating = notFive; 23 }
由於這樣的原因,你不能擔保類在初始化時就是有效的,如果你想要保證你的結構體一直處於一個有效狀態,你需要讓你的團隊避免使用無參構造函數。
注意繼承限制
結構體自動繼承自ValueType類,ValueType類繼承自object(CSDN:ValueType為值類型提供基類)。但是不能繼承自他們:
1 public struct InheritingFlower : Flower //<- Compile Error 2 { 3 }
盡管如何,可以使用接口繼承:
1 public struct ImplementingFlower : IComparable<ImplementingFlower> 2 { 3 string name; 4 int beautyRating; 5 6 public int CompareTo(ImplementingFlower other) 7 { 8 return beautyRating.CompareTo(other.beautyRating); 9 } 10 }
使用結構體的最佳方式
結構體做為一種簡單的數據結構很閃亮。很容易玩轉struct結構體,當需要代表數據傳輸對象,數據輸入對象、數據輸出對象,或者其它信息持有者。當我們將它們做為一種簡單的結構形式,沒必要封閉在屬性里面。
1 public struct PropertyFlower 2 { 3 string name; 4 int beautyRating; 5 bool isVascular; 6 7 public string Name { get => name; set => name = value; } 8 public int BeautyRating { get => beautyRating; set => beautyRating = value; } 9 public bool IsVascular { get => isVascular; set => isVascular = value; } 10 }
做為替代,可以讓字段自由運行,如下:
1 public struct FieldFlower 2 { 3 public string name; 4 public int beautyRating; 5 public bool isVascular; 6 }
一定要避免將結構體做為collecting parameter使用。如果將結構體傳遞給了方法函數,它是值傳遞,傳遞的是struct的副本,不是struct的指針。
1 class StructPass 2 { 3 public void changeName(Flower flower) 4 { 5 flower.name = "different"; 6 } 7 } 8 9 [Fact] 10 public void Structs_Pass_By_Value() 11 { 12 Flower flower = new Flower("daisy", 3, true); 13 new StructPass().changeName(flower); 14 15 Assert.NotEqual("different", flower.name); 16 }
結構體是類的小兄弟,他們不與其它類一起出現,他們較少用到,但是他們有他們的用處。
結構體具有內置的平等性。他們不能為null,結構體以最簡單的方式制作出簡潔的結構數據。如果謹慎使用結構體,會物超所值。