程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#中struct和class的差別詳解

C#中struct和class的差別詳解

編輯:C#入門知識

C#中struct和class的差別詳解。本站提示廣大學習愛好者:(C#中struct和class的差別詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是C#中struct和class的差別詳解正文


本文具體剖析了C#中struct和class的差別,關於C#初學者來講是有需要加以懂得並控制的。

簡略來講,struct是值類型,創立一個struct類型的實例被分派在棧上。class是援用類型,創立一個class類型實例被分派在托管堆上。但struct和class的差別遠不止這麼簡略。

歸納綜合來說,struct和class的分歧表現在:

● 類是援用類型,struct是值類型
● 在托管堆上創立類的實例,在棧上創立struct實例
● 類實例的賦值,賦的是援用地址,struct實例的賦值,賦的是值
● 類作為參數類型傳遞,傳遞的是援用地址,struct作為參數類型傳遞,傳遞的是值
● 類沒有默許無參結構函數,struct有默許無參結構函數
● 類支撐繼續,struct不支撐繼續
● 類傾向於"面向對象",用於龐雜、年夜型數據,struct傾向於"簡略值",好比小於16字節,構造簡略
● 類的成員很輕易賦初值,很難給struct類型成員賦初值
● 類的實例只能經由過程new SomeClass()來創立,struct類型的實例既可以經由過程new SomeStruct()來創立,也能夠經由過程SomeStruct myStruct;來創立

1、從賦值的角度體驗struct和class的分歧

援用類型賦值,是把地址賦值給了變量

class Program
  {
    static void Main(string[] args)
    {
      SizeClass sizeClass = new SizeClass(){Width = 10, Length = 10};
      Console.WriteLine("賦值前:width={0},length={1}", sizeClass.Width, sizeClass.Length);

      var copyOfSizeClass = sizeClass;
      copyOfSizeClass.Length = 5;
      copyOfSizeClass.Width = 5;
      Console.WriteLine("賦值後:width={0},length={1}",sizeClass.Width, sizeClass.Length);
      Console.ReadKey();
    }
  }

  public class SizeClass
  {
    public int Width { get; set; }
    public int Length { get; set; }
  }

  public struct SizeStruct
  {
    public int Width { get; set; }
    public int Length { get; set; }
  }

運轉成果以下圖所示:

以上,當把sizeClass賦值給copyOfSize變量的時刻,是把sizeClass所指向的地址賦值給了copyOfSize變量,2個變量同時指向統一個地址。所以,當轉變copyOfSizeClass變量的值,也相當於轉變了sizeClass的值。

struct類型賦值,是完整拷貝,在棧上多了一個完整一樣的變量

class Program
  {
    static void Main(string[] args)
    {
      SizeStruct sizeStruct = new SizeStruct(){Length = 10, Width = 10};
      Console.WriteLine("賦值前:width={0},length={1}", sizeStruct.Width, sizeStruct.Length);

      var copyOfSizeStruct = sizeStruct;
      copyOfSizeStruct.Length = 5;
      copyOfSizeStruct.Width = 5;
      Console.WriteLine("賦值後:width={0},length={1}", sizeStruct.Width, sizeStruct.Length);
      Console.ReadKey();
    }
  }

法式運轉成果以下圖所示:

以上,當把sizeStruct賦值給copyOfSizeStruct變量的時刻,是完整拷貝,轉變copyOfSizeStruct的值不會影響到sizeStruct。

2、從參數傳值角度體驗struct和class的分歧

援用類型參數傳遞的是地址

class Program
  {
    static void Main(string[] args)
    {
      List<string> temp = new List<string>(){"my","god"};
      temp.ForEach(t => Console.Write(t + " "));
      Console.ReadKey();
    }

    public static void ChangeReferenceType(List<string> list)
    {
      list = new List<string>(){"hello", "world"};
    }
  }

運轉成果:my god

為何不是hello world?
→棧上的temp指向托管堆上的一個聚集實例
→當temp放到ChangeReferenceType(temp)辦法中,實質是把temp指向的地址賦值給了變量list
→在ChangeReferenceType(List<string> list)辦法外部,又把變量list的指向了別的一個聚集實例地址
→但temp的指向地址一向沒有轉變

我們再來轉變ChangeReferenceType(List<string> list)外部完成方法,其它不變。

class Program
  {
    static void Main(string[] args)
    {
      List<string> temp = new List<string>(){"my","god"};      
      ChangeReferenceType(temp);
      temp.ForEach(t => Console.Write(t + " "));
      Console.ReadKey();
    }

    public static void ChangeReferenceType(List<string> list)
    {
      list.Clear();
      list.Add("hello");
      list.Add("world");
    }
  }

運轉成果:hello world

為何不是my god? 
→棧上的temp指向托管堆上的一個聚集實例
→當temp放到ChangeReferenceType(temp)辦法中,實質是把temp指向的地址賦值給了變量list
→在ChangeReferenceType(List<string> list)辦法外部,把temp和list配合指向的實例清空,又添加"hello"和"world"2個元素
→因為list和temp指向的實例是一樣的,所以轉變list指向的實例就同等於轉變temp指向的實例

以上,很好地解釋了:援用類型參數傳遞的是地址。

值類型struct參數傳遞的是值

class Program
  {
    static void Main(string[] args)
    {
      Size s = new Size(){Length = 10, Width = 10};
      ChangeStructType(s);
      Console.Write("Length={0},Width={1}", s.Length,s.Width);
      Console.ReadKey();
    }

    public static void ChangeStructType(Size size)
    {
      size.Length = 0;
      size.Width = 0;
    }
  }

  public struct Size
  {
    public int Length { get; set; }
    public int Width { get; set; }
  }

運轉成果以下圖所示:

為何Length和Width不是0呢?
→在棧上變量size
→當經由過程ChangeStructType(size),把s變量賦值給ChangeStructType(Size size)中的size變量,其實質是在棧上又創立了一個變量size,size的值和s是完整一樣的
→在ChangeStructType(Size size)外部轉變size的值,與變量s毫有關系

3、從struct類型的struct類型屬性和struct援用類型屬性體驗struct和class的分歧

假定有一個struct,它有struct類型的屬性

以下, struct類型Room有struct類型的屬性TableSize和TvSize,我們若何經由過程Room實例來修正其struct類型的屬性值呢?

class Program
  {
    static void Main(string[] args)
    {
      Room r = new Room()
      {
        TableSize = new Size(){Length = 100, Width = 80},
        TvSize = new Size(){Length = 10, Width = 8}
      };

      r.TableSize.Length = 0;
      
      Console.WriteLine("table今朝的尺寸是:length={0},width={1}", r.TableSize.Length, r.TableSize.Width);
      Console.ReadKey();
    }
  }

  public struct Size
  {
    public int Length { get; set; }
    public int Width { get; set; }
  }

  public struct Room
  {
    public Size TableSize { get; set; }
    public Size TvSize { get; set; }
  }

以上,r.TableSize.Length = 0;此處會報錯:不克不及修正r.TableSize的值,由於不是變量。切實其實,r.TableSize只是Size的一份拷貝,並且也沒有賦值給其它變量,所以r.TableSize是暫時的,會被主動收受接管,對其賦值也是沒成心義的。

 假如要修正r.TableSize,只需把

r.TableSize.Length = 0;

改成以下:

r.TableSize = new Size(){Length = 0, Width = 0};

運轉成果以下圖所示:

可見,轉變struct類型的struct類型屬性的某個屬性是行欠亨的,由於像以上r.TableSize只是一份拷貝,是暫時的,會被主動收受接管的。要轉變struct類型的struct類型屬性,就須要像下面一樣,給r.TableSize賦上一個完全的Size實例。

假定有一個struct,它有援用類型的屬性呢?

以下,struct類型的Room有援用類型屬性,TableSize和TvSize,若何經由過程Room實例來修正其援用類型的屬性值呢?而且,我們在類Size中界說了一個事宜,當給Size的屬性賦值時就觸發事宜,提醒size類的屬性值產生了轉變。

class Program
  {
    static void Main(string[] args)
    {
      var oneSize = new Size() {Length = 10, Width = 10};
      var twoSize = oneSize;

      oneSize.Changed += (s, e) => Console.Write("Size產生了轉變~~");
      oneSize.Length = 0;
      Console.ReadKey();
    }
  }

  public class Size
  {
    private int _length;
    private int _width;

    public event System.EventHandler Changed;

    public int Length
    {
      get { return _length; }
      set
      {
        _length = value;
        OnChanged();
      }
    }

    public int Width
    {
      get { return _width; }
      set { _width = value; OnChanged(); }
    }

    private void OnChanged()
    {
      if (Changed != null)
      {
        Changed(this, new EventArgs());
      }
    }
  }

  public struct Room
  {
    public Size TableSize { get; set; }
    public Size TvSize { get; set; }
  }

運轉,顯示:Size產生了轉變~~

對oneSize.Length的修正,現實上修正的是oneSize.Length指向托管堆上的實例。

4、從結構函數體驗struct和class的分歧

struct類型包括隱式的默許無參結構函數

class Program
  {
    static void Main(string[] args)
    {
      var size = new SizeStruct();
      Console.WriteLine("length={0},width={1}", size.Length, size.Width);
      Console.ReadKey();
    }
  }

  public struct SizeStruct
  {
    public int Length { get; set; }
    public int Width { get; set; }
  }

運轉成果以下圖所示:

為何我們沒有給SizeStruct界說無參結構函數,而沒有報錯?
--由於,struct類型有一個隱式的無參結構函數,而且給一切的成員賦上默許值,int類型屬性成員的默許值是0。

類不包括隱式無參結構函數

class Program
  {
    static void Main(string[] args)
    {
      var size = new SizeClass();
      Console.WriteLine("length={0},width={1}", size.Length, size.Width);
      Console.ReadKey();
    }
  }

  public class SizeClass
  {
    public int Length { get; set; }
    public int Width { get; set; }

    public SizeClass(int length, int width)
    {
      Length = length;
      Width = Width;
    }
  }

運轉,報錯:SizeClass不包括0個參數的結構函數

5、從給類型成員賦初值體驗struct和class的分歧

假如直接給字段賦初值。

public struct SizeStruct
  {
    public int _length = 10;
  }

運轉,報錯:構造中不克不及有實例字段初始值設定項

假如經由過程結構函數給字段賦初值。

public struct SizeStruct
  {
    public int _length;

    public SizeStruct()
    {
      _length = 10;
    }
  }

運轉,報錯:構造中不克不及包括顯式無參數結構函數

可見,給struct類型成員賦初值是不太輕易的,而給class成員賦初值,no problem。

什麼時候應用struct,什麼時候應用class?

在多半情形下,推舉應用class類,由於不管是類的賦值、作為參數類型傳遞,照樣前往類的實例,現實拷貝的是托管堆上援用地址,也就年夜概4個字節,這異常有助於機能的晉升。

而作為struct類型,不管是賦值,作為參數類型傳遞,照樣前往struct類型實例,是完整拷貝,會占用棧上的空間。依據Microsoft's Value Type Recommendations,在以下情形下,推舉應用struct:

● 小於16個字節
● 傾向於值,是簡略數據,而不是傾向於"面向對象"
● 願望值弗成變

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved