重學c#————struct

前言

簡單整理一下struct。c#

正文

struct

對於struct 而言呢,咱們每每會拿class做爲對比,可是呢,咱們在初學階段用class來替代struct,struct的存在感愈來愈低了。數組

那麼是什麼緣由使咱們常用struct呢?我感受很簡單的一句話就是struct能作的class都能作,struct不能作的,class 也能作,這就是問題關鍵了。安全

那麼先來看下他們的對比:異步

一、結構是值類型,它在棧中分配空間;而類是引用類型,它在堆中分配空間,棧中保存的只是引用。函數

二、結構類型直接存儲成員數據,讓其餘類的數據位於堆中,位於棧中的變量保存的是指向堆中數據對象的引用。性能

  1. 結構不支持繼承。this

  2. 結構不能聲明默認的構造函數。code

  3. 結構類型中不能設置默認值。對象

從第二點中能夠明白結構類型中,不必定存儲的必定是值,還多是引用,這就打破了初學的時候誤覺得結構類型只能存儲值類型,還多是引用對象的引用,以下:繼承

static void Main(string[] args)
{
	var parent = new Parent(30,"張大大");
	var zhangsan = new Student(1,"張三",parent);
	zhangsan.age = 10;
}
struct Student {
	public Student(int age, string name,Parent parent)
	{
		this.age = age;
		this.name = name;
		this.parent = parent;
	}
	public int age { get; set; }
	public string name { get; set; }
   
	public Parent parent { get; set; }
}

struct Parent {
	public Parent(int age, string name)
	{
		this.age = age;
		this.name = name;
	}
	public int age { get; set; }
	public string name { get; set; }
}

在Student 結構中,咱們也能夠去複製引用。

第三點很好理解,第四點表示咱們不能去本身聲明默認構造函數。如:

public Student() { 
}

那麼咱們何時使用struct呢?

那麼要從struct 優勢出發,struct 是值類型,當離開做用域的時候,那麼對垃圾回收是有好處的。

一樣,由於struct 是值類型,分配到堆上,若是值類型過大,這會大量佔用到堆的空間,因此咱們的數據比較下。

當有大量的賦值語句的時候,那麼咱們也應該避開struct,由於賦值值類型中將會拷貝所有,而不是引用。

根據上訴,實用場景爲:

對於點、矩形和顏色這樣的輕量對象,假如要聲明一個含有許多個顏色對象的數組,則CLR須要爲每一個對象分配內存,在這種狀況下,使用結構的成本較低;

從上總結出,struct能夠在一些以數據爲主的場景中使用,且數據量不大的狀況。

struct 做爲參數

在介紹readonly 以前,先介紹一下,和ref 還有out 其名的in,不是別的in哈。

static void Main(string[] args)
{
	int readonlyArgument = 44;
	InArgExample(readonlyArgument);
	Console.WriteLine(readonlyArgument);     // value is still 44
}
static void InArgExample(in int number)
{
	// Uncomment the following line to see error CS8331
	//number = 19;
}

這裏的in 的做用是能夠引用readonlyArgument,可是隻讀,不能修改number的值。那麼這有什麼用呢?我直接不設置值不就能夠嗎?或者說我起碼設置一個readonly 這總行吧。

而咱們知道in 有不能用於異步方法,賦值消耗也不大形參,那我要這個引用有啥用?關鍵就在於咱們自定義的struct仍是大有好處的,struct 是咱們自定義的結構類型,這個比較大,那麼這就是一個struct的突破點了,傳值的時候能夠傳遞struct。

下面介紹readonly 這個是爲了安全,作爲一個readonly,咱們首先就要區分的是const,const 是編譯性,而readonly是運行時。這個能夠百度,在此就不作過多的介紹。

經過readonly struct 還有 in,那麼能夠建立防護性副本。

這裏值得注意的是,官網提到這樣一句話:

除非使用 readonly 修飾符聲明 struct或方法僅調用該結構的 readonly 成員,不然切勿將其做爲 in 參數傳遞。 不遵照該指南可能會對性能產生負面影響,並可能致使不明確的行爲

官網給出了一個這樣的例子:

int readonlyArgument = 44;
InArgExample(readonlyArgument);
Console.WriteLine(readonlyArgument);     // value is still 44

void InArgExample(in int number)
{
    // Uncomment the following line to see error CS8331
    //number = 19;
}

而且說明了一段這樣的話:

在首次檢查時,你可能認爲這些訪問是安全的。 畢竟,get 訪問器不該該修改對象的狀態。 可是沒有強制執行的語言規則。 
它只是通用約定。 任何類型均可以實現修改內部狀態的 get 訪問器。 
若是沒有語言保證,編譯器必須在調用任何未標記爲 readonly 修飾符的成員以前建立參數的臨時副本。 
在堆棧上建立臨時存儲,將參數的值複製到臨時存儲中,並將每一個成員訪問的值做爲 this 參數複製到堆棧中。 
在許多狀況下,當參數類型不是 readonly struct,而且該方法調用成員未標記爲 readonly 時,這些副本會下降性能,
使得按值傳遞比按只讀引用傳遞速度更快。 若是將不修改結構狀態的全部方法標記爲 readonly,編譯器就能夠安全地肯定不修改結構狀態,而且不須要防護性複製。

struct 做爲出參

那麼上面討論了參數傳遞的問題,那麼接下來討論一下,參數返回的問題。

好比說返回了:

var a=getANumber();
private static int getANumber(){
   var b=1;
   return b;
}

那麼其實這個a的值怎麼獲取的呢?是b的值賦值給a。

可是對於比較大的struct,用這種賦值的方式,就比較消耗cpu和內存了。

那麼可使用ref來返回。

var a=getANumber();
private static ref int getANumber(){
   var b=1;
   return ref b;
}

這樣b的引用傳遞給了a。

若是你但願返回的參數不可改變,那麼你能夠這樣:

var a=getANumber();
private static ref readonly int getANumber(){
   var b=1;
   return ref b;
}

那麼這個時候有人就奇怪了,爲啥ref還要 readonly 這東西呢?

舉個例子:

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

這個例子返回的是數組的一部分,若是改了這個值,那麼數組裏面的值不就改變了嗎。

可能我這樣說,加上官網這個例子不到位,可能沒能表達明白。再來一個本身寫的例子:

static void Main(string[] args)
{
	var matrix =new Student[1];
	matrix[0] = new Student(20,"張三",new Parent());
	ref Student result =ref Find(matrix);
	result.age = 10;
	Console.WriteLine(matrix[0].age);
	Console.WriteLine(result.age);
	Console.ReadLine();
}

static ref Student Find(Student[] matrix )
{
	return ref matrix[0];
}
struct Student {
	public Student(int age, string name,Parent parent)
	{
		this.age = age;
		this.name = name;
		this.parent = parent;
	}
	public int age { get; set; }
	public string name { get; set; }
   
	public Parent parent { get; set; }
}

這裏打印出來兩個都是10。

若是給Find 加上readonly,那麼要這樣寫

ref readonly Student result =ref Find(matrix);

Student 天然不能再進行賦值。

上述只是我的整理和一點點我的理解,若有不對望指出。下一節,整理c# 裝箱和拆箱,介紹一下生命週期。

相關文章
相關標籤/搜索