結構的小小心得筆記

有一天我同事給我看一段code

問我有關於結構的問題

一直以來我也沒什麼用到結構過

只知道結構是用來設計小型的類別(主要由欄位組成)

有一天我同事給我看一段code

問我有關於結構的問題

一直以來我也沒什麼用到結構過

只知道結構是用來設計小型的類別(主要由欄位組成)

是實值型別,將結構值派給新變數是複製一份過去而不是將參考給新變數而以…

我看到他的程式中有一段code是這樣寫的

line myLine;
myLine.starting.x = 1;
myLine.starting.y = 4;
myLine.ending.x = 10;
myLine.ending.y = 11;
myLine.length();

其實我第一眼看到是馬上說"這寫錯了,不能跑吧"

但是事實就是他能跑,不需要初始化(new)

但是我將2、3、4、5行砍掉後結果又不能跑了

變成要初始化才能

試到這邊的結論是結構是不需要初始化的

但是有一個條件,就是結構中沒有任何欄位

這邊有一個簡單的測試

public struct StructEmployee
{
    public string Name;
    public int Age;
    public string GetDetail()
    {
        return "Name : " + Name + ", Age : " + Age.ToString();
    }
}

static void Main(string[] args)
{
    StructEmployee ts;
    ts.Age = 20;
    ts.Name = "cloudio";
    Console.WriteLine(ts.GetDetail());       
}

這程式跑起來當然是輸出"cloudio"

但是如果我把Age改成private呢?

改成private後當然就不能在用

ts.Age = 20;這段了

所以我就把他mark掉

變成這樣

public struct StructEmployee
{
    public string Name;
    private int Age;
    public string GetGetDetail()
    {
        return "Name : " + Name + ", Age : " + Age.ToString();
    }
}

static void Main(string[] args)
{
    StructEmployee ts;        
    ts.Name = "cloudio";
    Console.WriteLine(ts.GetDetail());            
}

OK,又掛了

這代表不管欄位是public或是Private都要給值才能不用初始化就使用他的method

所以我改寫成

public struct StructEmployee
{
    public StructEmployee(int age)
    {
        Age = age;               
    }
    public string Name;
    private int Age;

    public string GetDetail()
    {
        return "Name : " + Name + ", Age : " + Age.ToString();
    }
}

此時又多了一個新的錯誤,我必須要在建構式中設定所有的欄位才能使用Orz…

所以最後程式改成這樣

public struct StructEmployee
{
    //把Name封裝起來
    private string _name;
    private int _age;
    public StructEmployee(string name, int age)
    {
        _name = name;
        _age = age;
    }

    #region Property
    /// <summary>
    /// 名子
    /// </summary>
    public String Name
    {
        get
        {
            return _name;
        }
    }

    /// <summary>
    /// 年齡
    /// </summary>
    public int Age
    {
        get
        {
            return _age;
        }
    }
    #endregion

    public string GetDetail()
    {
        return "Name : " + _name + ", Age : " + _age.ToString();
    }
}

static void Main(string[] args)
{
    StructEmployee ts = new StructEmployee("cloudio", 29);
    //ts.Name = "cloudio";
    Console.WriteLine(ts.GetDetail());
}

接下來我又試著設計一個class叫做Department,想當然此物件是要給StructEmployee使用的

public struct Department
{
    private string _name;

    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
        }
    }

    public Department(string employeeName)
    {
        _name = string.Empty;
        _name = getDeptNameUseEmployeeName(employeeName);
    }

    private string getDeptNameUseEmployeeName(string arg)
    {
        string _result;
        if (arg == "cloudio")
        {
            _result = "資訊部";
        }
        else if (arg == "bill")
        {
            _result = "業務部";
        }
        else
        {
            _result = "未分派";
        }
        return _result;
    }
}

實務上getDeptNameUseEmployeeName的資料來源會更有可能來自於資料庫

原來在StructEmployee中的建構式因為我多了一個dept的欄位所以也要在設定dept結果StructEmployee變成

private Department _department;

public Department Department
{
    get
    {
        return _department;
    }
}

public StructEmployee(string name, int age)
{
    _name = name;
    _age = age;
    _department = new Department(_name);
}

原本我在寫的過程本來Department的建構式是寫成要傳入StructEmployee的

但是後來想想這樣這兩個物件太親密了,到時要分手麻煩就放棄了說XD

 

這樣一路邊嘗試邊寫感覺有點漸漸照著.NET規劃的Struct設計方式在走(我沒寫過其他的OO語言)

最後Struct給我的設計感覺是理面的所有欄位應該都是互相有關聯的

這點可以在不論使用何種參數的建構式都一樣必須要在建構式中完成所有欄位的賦值感受到

struct的其它設計方式我覺得看看DateTime應該就會很有感覺了

這樣跑完一輪想必以後對Struct的設計方式應該會多了一點點的瞭解吧

測試過程如果有觀念錯誤煩請指正,謝謝

 

以下全是MSDN中的筆記

 

類別是參考型別 (Reference Type)

建立類別的物件時,物件所指派的變數僅持有該記憶體的參考。將物件參考指派給新變數時,新變數會參考原始物件。對其中一個變數進行的變更,會反映到另一個變數中,因為這兩個變數參考相同資料。

結構是實值型別 (Value Type)。建立結構時,結構所指派的變數會持有結構的實際資料。將結構指派給新變數時,會進行複製。因此,新變數和原始變數總共包含兩份相同資料的不同複本。對其中一份複本進行的變更,不會影響另一份複本。

我先幫

StructEmployee ts = new StructEmployee("cloudio", 29);
StructEmployee ts1 = ts;
Console.WriteLine(ReferenceEquals(ts, ts1));

結構與類別所使用的語法幾乎相同,不過結構的限制比類別多:

  • 結構宣告內不能初始化欄位,除非將其宣告為 const 或 static。

  • 結構不可宣告預設建構函式 (沒有參數的建構函式) 或解構函式。

  • 結構無法繼承自類別或其他結構。

  • 結構是在指派時複製的。當指派結構給新變數時,就會複製所有資料,而新變數所做的任何修改都不會變更原始複本的資料。

  • 結構為實值型別,而類別則是參考型別。

  • 與類別不同的是,結構不需使用 new 運算子就能執行個體化

  • 結構可以宣告含有參數的建構函式。

  • 結構無法從另一個結構或類別繼承而來,且它不能成為類別的基底。所有結構都是從繼承自 System.Object 的 System.ValueType 直接繼承而來

  • 結構可實作介面

  • 結構可以用來當做可為 Null 的型別,而且可以對其指派 null 值。

 

除非型別具有下列所有特性,否則不要定義結構:

  • 它會以邏輯方式表示單一值,類似於基本型別 (整數、雙精度浮點數 (Double) 等)

  • 它的執行個體小於 16 個位元組

  • 它是不變的

  • 它不需經常進行 Boxed 處理

如果未滿足其中一個或多個條件,則要建立參考型別,而非結構。無法遵守這個方針可能會對效能有負面的影響。