天天看點

C#/WPF學習筆記:class 與 struct

初學C#,知道一句話:“一切都是對象”。這是C#與C++一個很大的差別。同時,對于引用類型的對象來說,指派操作傳遞的是一個引用,而不是值。初學時,為了切換到C#的頻道,想當然的認為所有的指派操作均是如此,但是這樣想有時會造成嚴重的錯誤。原因在于,C#中,除了引用類型,還存在一類很重要的類型,實際上也是所有程式員都很熟悉的類型:值類型。

值類型與引用類型的最大差別在與記憶體配置設定,一般而言值類型的變量,是在棧上進行配置設定,引用類型,在托管堆配置設定。

一、指派操作

我們來看一個例子:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Collections;

namespace ConsoleApplication1

{

    class Program

    {

        public class Person

        {

            public string Name;

            public override string ToString()

            {

                return Name;

            }

        }

        static void Main(string[] args)

        {

            ArrayList members = new ArrayList();

            Person p = new Person();

            p.Name = "Old name";

            members.Add(p); // [A]

            Person p2 = ((Person)members[0]); // [B]

            p2.Name = "New Name";

            Console.WriteLine("Value is: " + members[0].ToString());

        }

    }

}

輸出是什麼?

由于C#中,“對象指派是傳遞執行個體的引用”,是以,上面[B]處的代碼,實際上是取得了數組中的第一個元素,類似C++中得到了第一個元素的位址,後面的操作實際上是直接操作了數組中第一個元素,是以輸出會是修改後的值“New Name”。

現在,我們把“Person” 的定義更改為結構。看看輸出發生了什麼變化。我們發現,輸出的仍是初始化時的值。

這是為什麼?

這就是引用類型與值類型的不同。“對象指派是傳遞執行個體的引用”,這句話隻針對引用類型。對于值類型來說,指派操作,就是資料拷貝。當Person是一個結構時,在[A]處,進行了裝箱(Boxing),在[B]處,進行了拆箱(Unboxing),變量 p2 是一個不同的對象,是數組第一個元素的完整拷貝。

綜合以上考慮,微軟在C#程式設計向導中規定,盡可能不要使用Struct,除非要定義的結構滿足下面的特征:

  • 它在邏輯上表示單個值。
  • 它的執行個體大小小于 16 位元組。
  • 它是不可變的。
  • 它不必頻繁被裝箱。

二、this指針

準确的說應該是this對象。從語義上講,C++的this與C#一樣,都指向該類的目前執行個體。不同的是,C++的this是一個指針,C#沒有指針。另外,C++中,一般情況下this指針不允許在構造函數中使用,因為此時,對象尚未建立完成。C#則可以,可以了解為,當構造函數進入時,類的執行個體已經建立了,this有效。這也是為什麼我們可以再C#的構造函數中使用this指針來初始化類成員。

三、構造函數

構造函數。C#中的結構和類在處理預設構造函數時有較大差別。差別就是,struct總是有一個系統預設的構造函數,并且不允許自定義構造函數。原因是必須保證值類型的初始化狀态。

Hi Paitum,

Structs and constructors behave a bit differently from classes. In classes,

an instance must be created by calling new before the object is used; if

new isn't called, there will be no created instance, and the reference must

be null.

There is no reference associated with a struct, however. If new isn't

called on the struct, an instance that has all of its fields zeroed is

created. In some cases, user can then use this instance without further

initialization.

It is therefore important to make sure that the all-zeroed state is a valid

initial state for all the value types.

A default(parameterless) constructor for a struct could set different

values than the all-zeroed state which would be unexpected behavior. The

.Net Runtime therefore prohabits default constructors for struct.

Best Regards,

Billy Zhang

Microsoft

class 沒有這個限制,理論上講,我們不需要像C++那樣為每一個類寫預設無參構造函數,因為.NET會為你寫一個,這裡有一個例外,就是當你自己寫了一個帶參數的構造函數後,編譯器就不再為你寫無參構造函數。是以,這裡有一個衍生規則:任何情況下,都自己寫一個預設構造函數。

Thanks:

1. 了解C#值類型與引用類型

2. Structs cannot contain explicit parameterless constructors. WHY?

3. Effective C#: 50 Specific Ways to Improve Your C#

4. Structure Design Guidelines, Value Type Usage Guidelines, in MSDN.

繼續閱讀