C# struct


簡介

C/C++程序員或多或少都有使用struct的經歷,在C++structclass的區別不大,除了默認成員的可訪問性,這點在C#中則截然不同。本文將力圖說明C#structclass的區別以及如何正確的使用struct

為什么需要struct?

眾所周知,在java中並沒有struct的概念,那么C#為何引入struct呢?最基本原因是可以創建值類型的類型,使在托管環境中有更好的性能。

區別於javaC#有值類型和引用類型的概念(java只有引用類型)。引用類型的實例分配在堆上,當對象沒有被引用時被垃圾回收器回收;值類型的實例分配在棧上,當離開其作用域后內存被回收。值類型本身存儲的是值本身,引用類型存儲的是引用,C#語言提供的原始類型除了string類型其它都是值類型。

C#struct是值類型,class是引用類型,可以通過enumstruct關鍵字創建值類型的對象。使用值類型可以減少托管堆上對象的數量,從而減少垃圾回收器(GC)的負擔,提高性能,值類型也有明顯的缺點:通過值類型傳遞較大對象的開銷比較昂貴、裝箱和拆箱對性能造成影響。

Classes 和Structs

 

 public struct Employeestruct
    {
        private int _fooNumber;

        public Employeestruct(int fooNumber)
            : this()
        {
            _fooNumber = fooNumber;
        }

        public string Name { get; set; }

        public int Age { get; set; }

        public string GetMessage()
        {
            return string.Format("{0}--{1}", Name, Age);
        }
    }

 

從上面可以看到,structclass非常相似,不過它們還是有本質的區別,接下來我們就一一分析。

1. Structs 和Inheritance

StructsSystem.ValueType繼承而classes繼承於System.Object或從System.Object繼承的其他類型,當然System.ValueType也從System.Object繼承(這不是重點)Structs可以實現接口,但不能從另一個classesstructs繼承,而且不能作為其他classes的基類。要知道,當你把structs作為接口使用時,就進行一次隱形裝箱,因為接口作為引用類型。請看下面代碼:

struct Foo : IFoo
{ 
      int x;
}

IFoo iFoo = new Foo();

 

Foo的實例被創建並賦值給iFoo(裝箱),當iFoo調用時實際上使用的是Foo裝箱后的實例。

2. Constructors

盡管CLR允許,但是不允許在structs中定義無參的構造函數。對於值類型,編譯器默認情況下不生成默認的構造函數,也不調用默認的構造函數,所以C#編譯器不允許使用者定義無參的構造函數。由於structs不生成默認的構造函數,所以不能初始化字段。如下(錯誤):

Struct MyFoo
{
        int x = 1;
}

 

記住,編譯器把初始化工作放在構造函數中,由於structs沒有默認的構造函數,所以不能初始化字段。

有趣的是,你可以使用下面的語法:

Foo foo  =  new Foo();

通過之前的章節了解到,盡管初始化foo使用了new操作符,但是structs的實例分配到棧上。new Foo()不會調用無參的構造函數,僅僅是初始化該struct的字段為默認值。

 

struct Foo
{
       int x;

       public Foo(int x)
       {
           this.x = x;
       }
}    

Foo foo  =  new Foo();

 

注意,我已經重載了構造函數,然而我能夠調用new Foo()

3. Destructors

Structs不允許定義析構函數,如果你扔就去定義一個析構函數,編譯器會立即提示一個錯誤,不過structs可以實現IDisposable接口。

4. Comparison against null

Structs不能和null進行比較,這點在.net framework2.0已不再是問題(可空類型),關於可空類型,超出本文討論范圍。

5. Readonly關鍵字

對於引用類型,readonly阻止再為變量賦值,但不阻止你修改當前引用對象的狀態。對於值類型,readonly有點類似C++中的const關鍵字,它阻止你修改引用對象,也意味着不能再為他賦值,因為這會導致重新初始化一次。請看下面代碼:

    public class MyReferenceType
    {
        public int State { get; set; }
    }

    public struct MyValueType
    {
        public int State { get; set; }
    }

   public void TestMethod()
    {
        myReferenceType = new MyReferenceType(); // 錯誤
        myReferenceType.State = 1234; // Ok

        myValueType = new MyValueType(); // 錯誤
        myValueType.State = 1234; //  錯誤
    }

 

foreach 語句和using語句的變量為隱式readonly,所以如果使用structs,將無法更改其狀態。

何時使用 structs

通過前面的描述已經清楚classesstructs的區別,那我們來看下適合使用structs的場景:

l 實例使用起來像C#的基元類型

l 需要創建大量的、短暫實例(例如在循環體內)

l 實例不需要大量的傳遞

l 不需要從其他類型繼承或不希望被其他類型繼承

l 希望被人操作你實例的副本

不適合使用structs的場景:

l 實例過大,當實例過大時,傳遞實例會有很大的開銷。微軟建議structs的理想大小應該在16bytes以下

l 當回引起裝箱、拆箱操作時。關於裝箱、拆箱超出本文討論范圍


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM