值類型和引用類型詳解(C#)

可能你對值類型和引用類型還不太了解。

值類型和引用類型,是c#比較基礎,也必須掌握的知識點,但是也不是那么輕易就能掌握,今天跟著我一起來看看吧。

典型類型

首先我們看看這兩種不同的類型有哪些比較典型的代表。

典型值類型

int, long, float, double等原始類型中表示數字的類型都是值類型,表示時間的datetime也是值類型,除此之外我們還可以通過關鍵字struct自定義值類型。

典型引用類型

原始類型中,array, list, dictionary, queue, stack和string都是引用類型,除此之外我們通過關鍵字class自定義引用類型。

基類

c#中所有的類型都最終繼承自 Object ,這是沒有疑問的,但是這其中還有些微區別。

值類型基類

對于值類型來說,除了最終繼承自Object,還繼承自ValueType,繼承鏈如下

添加圖片注釋,不超過 140 字(可選)

但是請不要誤解,這里僅僅指的是值類型天然是ValueType,但是不代表值類型能夠這么聲明

struct Struct1 : ValueType { }

這樣是會引起編譯錯誤的,值類型不能繼承任何其他類型,值類型只能實現接口,不能繼承自其它類型只有引用類型既可以實現接口也能繼承自其它類型。順便說一下,還有一點比較重要的是,ValueType重寫了Object基類的 Equals 方法和 GetHashCode 方法,所以當使用Equals比較兩個值類型的時候,系統會比較兩個值類型的各個屬性是否相等,再返回結果,這就是所謂的相等性 。與此相對,引用類型在使用Equals的時候,會在后臺調用object.ReferenceEquals,換言之,引用類型在比較相等性的時候會考慮同一性 。

引用類型基類

對于引用類型就沒有那么麻煩,引用類型不會繼承自ValueType。引用類型可以繼承其他類型。

在內存中的表現

我們都知道,C#將內存分為了兩部分,一個是Stack,另外一個是Managed Heap。一般來說,用于函數調用進棧,函數返回出棧,用的是Stack,而當創造一個新的實例時,會根據創建的實例屬于值類型還是引用類型決定使用Stack還是Managed Heap。

值類型在內存中

當創建一個值類型對象時,c#會在Stack上面創建一塊空間,這塊空間就存放這個值類型對象。

int是一個典型的值類型,如下語句

int age = 10;

會存在于內存中的Stack上面。

添加圖片注釋,不超過 140 字(可選)

如果把值類型的實例賦值給另外一個值類型,那么效果就是復制一個新的值類型實例。

int myAge = age;

添加圖片注釋,不超過 140 字(可選)

引用類型在內存中

與值類型在內存中的表現不一樣,創建一個引用類型的實例,不但會在Stack上面新建一個引用,還會在Heap上面劃分出內存以容納該引用類型實例。用戶在使用的時候通過Stack上面的變量間接引用該實例。

class Author { public string Name{get;set;} public int Age{get;set;} } Author author = new Author(){Name="deatharthas", Age= 32};

添加圖片注釋,不超過 140 字(可選)

注意看和值類型在內存中的區別,引用類型通過Stack上的變量訪問位于Heap上面的實例。

在賦值的時候,拷貝的僅僅是Stack上面的變量,新拷貝出來的對象和舊的對象指向的是同一塊內存。

Author myAuthor = author;

添加圖片注釋,不超過 140 字(可選)

這個時候,author和myAuthor指向同一塊內存,稱為同一性,通過調用

object.ReferenceEquals(myAuthor, author);

可以得到驗證。

但可能有細心的朋友會有疑問了,不是說int是值類型,值類型是存在于Stack上面的嗎?為什么在author類里面,它會在Heap里面呢?贊一個細心!值類型一般存在于Stack上面,但如果某個值類型包含于引用類型,那么它也會隨著那個引用類型存放在Heap上面。

當參數時的行為區別

c#中的參數傳遞默認都是傳值(by value),但是根據所傳遞對象是值類型還是引用類型,它們的行為還是有所區別,現在我們來看看。

值類型當參數

值類型當參數的時候,傳遞到函數內部的是一份值類型的拷貝,所以在函數內部修改這個拷貝不會影響原對象。除非我們在傳遞參數的時候使用了ref或者out。

引用類型當參數

如果參數是引用類型,傳遞到函數內部的依然是一份拷貝,但是這個拷貝是其在Stack上面的變量的拷貝,就像上面的賦值那個例子。所以這個時候這份拷貝其實和原對象指向同一塊內存(指向同一性),修改這個對象可以反映到原對象上面。

謹慎返回引用類型

編程是一項需要謹慎的工作,有時候我們經常會犯一些錯誤,而這些錯誤又是那么的不明顯以至于不摔坑幾次,我們根本察覺不了,考慮下面一個例子。

class People 
{ 
public string Name{get;set;}
public int Age
{get;set;} 
private People _Father = null;
public People Father
{get{return _Father;}
}
public People(People father)
{_Father = father;
}
public void ShowFather()
{Console.WriteLine("father's name is " + Father.Name + " and his age is " + Father.Age);}
} 
class Program{static void Main(string[] args) 
{People father = new People(null){Name = "father", Age = 60 
};People son = new People(father);son.ShowFather(); Console.ReadLine();}
}

添加圖片注釋,不超過 140 字(可選)

看起來沒什么問題,對吧?Father沒有提供setter,似乎是安全的。但是我們試試下面的代碼。

static void Main(string[] args){ People father = new People(null){Name = "father", Age = 60 
}; 
People son = new People(father);var f = son.Father;f.Name="Changed";son.ShowFather();Console.ReadLine();}

添加圖片注釋,不超過 140 字(可選)

看,發現了什么,外部改變了本來應該被封裝所保護的Father屬性,封裝被破壞了!

稍微一想我們應該能明白這個道理,Father屬性返回的拷貝的變量和原Father變量指向同一塊實例。要想解決這個問題,我們要么返回一個值類型,要么返回一個全新的對象。修改Father屬性如下:

public People Father 
{ 
get 
{ 
return new People(_Father._Father){ 
Name = _Father.Name, Age = _Father.Age 
};} 
}

再次測試,

添加圖片注釋,不超過 140 字(可選)

這次封裝就沒問題了。

總結

我們大概知道了值類型和引用類型的區別,包括它們的行為,在內存的居住方式,以及使用引用類型時可能會遇到的暗坑,希望大家通過閱讀這篇文章,能夠加深一些對它們的了解,少走一些彎路。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/717340.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/717340.shtml
英文地址,請注明出處:http://en.pswp.cn/news/717340.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【云安全】網絡安全領域安全協議

IPSEC協議 IPSec(Internet Protocol Security)是一種網絡層安全協議,用于在IP通訊過程中確保完整性、認證性和機密性。它通過在標準的IP協議上加入安全機制來實現加密和認證。IPSec主要由兩個協議組成:認證頭(AH&…

在Windows 10系統中啟用快速啟動功能

在Windows 10系統中啟用快速啟動功能,可以按照以下步驟進行: 方法一(通過設置應用): 點擊任務欄左下角的“開始”按鈕或者按鍵盤上的Win鍵打開“開始”菜單。在“開始”菜單中選擇“設置”圖標(齒輪形狀&…

3.3日學習打卡----初學Redis(一)

3.3日學習打卡 目錄: 3.3日學習打卡NoSQL為什么要用NoSQL什么是NoSQL?NoSQL的四大分類關系型數據庫和非關系型數據及其區別NoSQL經典應用 RedisRedis是什么?Linux下安裝RedisDocker下安裝Redis基本知識 NoSQL 為什么要用NoSQL 單機Mysql的美好年代 在90年代&…

Sqlmap進行http頭注入及流量分析

環境準備:構建完善的安全滲透測試環境:推薦工具、資源和下載鏈接_滲透測試靶機下載-CSDN博客 利用 SQLMap 進行 HTTP 頭注入的方式對于 Less-19 注入點的注入 SQLMap 工具我使用kali中自帶的 注入準備 先使用bp將Less-19靶場的包抓下來保存到 txt 文件中,輸入賬號 admin…

Ubuntu23.10禁用Wayland

禁用前 編輯custom.conf文件 sudo vim /etc/gdm3/custom.conf 去掉WaylandEnablefalse前的#號 保存退出 重啟系統 生效: 成功轉換為X11

【LeetCode題解】2809. 使數組和小于等于 x 的最少時間+2788. 按分隔符拆分字符串+410. 分割數組的最大值

文章目錄 [2809. 使數組和小于等于 x 的最少時間](https://leetcode.cn/problems/minimum-time-to-make-array-sum-at-most-x/)思路: [2788. 按分隔符拆分字符串](https://leetcode.cn/problems/split-strings-by-separator/)思路: [410. 分割數組的最大…

Leetcoder Day36| 動態規劃part03

343. 整數拆分 給定一個正整數 n,將其拆分為至少兩個正整數的和,并使這些整數的乘積最大化。 返回你可以獲得的最大乘積。 示例 1: 輸入: 2輸出: 1解釋: 2 1 1, 1 1 1。 示例 2: 輸入: 10輸出: 36解釋: 10 3 3 4, 3 3 4 36。說明: 你可以假設 …

如何提取圖片中某個位置顏色的RGB值,RGB十進制值與十六進制的轉換

打開本地的畫圖工具,把圖片復制或截圖粘進去,用顏色提取器點對應的位置就可以提取了。 獲取到的 RGB 值為 (66,133,244) 轉化后的值為 #4285F4。 【內容拓展一】:RGB 十進制值與十六進制的轉換 當我們從 RGB 十進制值轉換為十六進制值時&a…

Yapi部署

【GO開發工程師】Yapi部署 推薦個人主頁:席萬里的個人空間 文章目錄 【GO開發工程師】Yapi部署1、Yapi部署 1、Yapi部署 初始化yapi: git clone https://github.com/Ryan-Miao/docker-yapi.git cd docker-yapi docker-compose upyapi啟動失敗 1.cd進入…

粉絲福利-純凈Windows系統安裝鏡像下載網站

?Windows操作系統鏡像文件是從微軟或其他經過驗證的來源下載正版操作系統安裝介質的關鍵所在。以下是詳細闡述從不同渠道獲取Windows系統鏡像的說明,尤其強調官方和安全的下載途徑。Windows系統鏡像可以從多個可靠來源下載,以下是幾個推薦的選擇: 微軟官方網站 微軟官方網…

對于《幻獸帕魯》這樣的游戲,如何優化服務器性能以提高游戲體驗?

對于《幻獸帕魯》這樣的游戲,如何評估和優化服務器性能以提高游戲體驗? 硬件配置優化:選擇高性能的服務器,如4核16G的幻獸帕魯服務器,這樣可以保證有足夠的計算性能和內存容量來支持游戲的運行。同時,考慮到…

Node.js(六)-數據庫與身份認證

一 、學習目標 ◆ 能夠知道如何配置MySQL數據庫環境 ◆ 能夠認識并使用常見的 SQL語操作數據庫 ◆ 能夠在Express中操作MySQL數據庫 ◆ 能夠了解 Session的實現原理 ◆ 能夠了解JWT的實現原理 二、數據庫的基本概念 1.1 什么是數據庫 數據庫(database)…

邊緣計算網關的重要作用-天拓四方

隨著物聯網技術的迅猛發展,數據量的爆炸式增長對數據處理和分析提出了更高的要求。邊緣計算網關作為連接物理世界和數字世界的橋梁,正逐漸受到各行業的重視。本文將從行業背景、功能特點以及帶來的效益等方面,探討邊緣計算網關在當前及未來的…

備戰藍橋杯---狀態壓縮DP基礎2之TSP問題

先來一個題銜接一下: 與上一題的思路差不多,不過這里有幾點需要注意: 1.因為某一列的狀態還與上上一行有關,因此我們令f[i][j][k]表示第i行狀態為j,第i-1行狀態為k的最大炮兵數。 因此,我們可以得到狀態轉移方程&…

2024/03/01

控制機械臂 #include<myhead.h> #define SER_IP "192.168.126.2" #define SER_PORT 8888#define CLI_IP "192.168.252.165" #define CLI_PORT 9999int main(int argc, const char *argv[]) {int cfdsocket(AF_INET,SOCK_STREAM,0);if(cfd-1){perror…

成功解決git clone遇到的error: RPC failed; curl 16 Error in the HTTP2 framing layer fatal: expected flush af

成功解決git clone遇到的error: RPC failed; curl 16 Error in the HTTP2 framing layer fatal: expected flush af 問題描述解決方案 問題描述 用git的時候可能會遇到這個問題&#xff1a; (base) zhouzikang7443-8x4090-120:~/project$ git clone https://github.com/123/12…

Windows服務器:通過nginx反向代理配置HTTPS、安裝SSL證書

先看下效果&#xff1a; 原來的是 http&#xff0c;配置好后 https 也能用了&#xff0c;并且顯示為安全鏈接。 首先需要 SSL證書 。 SSL 證書是跟域名綁定的&#xff0c;還有有效期。 windows 下雙擊可以查看相關信息。 下載的證書是分 Apache、IIS、Tomcat 和 Nginx 的。 我…

【leetcode】圓圈中最后剩下的數字

目錄 1. 問題 2. 思路 3. 代碼 4. 運行 1. 問題 本題即為典型的約瑟夫問題&#xff0c;通過遞推公式倒推出問題的解。原始問題是從n個人中每隔m個數踢出一個人&#xff0c;原始問題變成從n-1個人中每隔m個數踢出一個人…… 示例 1&#xff1a; 輸入: n 5, m 3 輸出: 3…

Unity TMP文字移動效果

前言 看見很多游戲有很特殊的波浪形文字效果&#xff0c;于是來嘗試一下控制TMP文字頂點的方式達到類似效果。 原理 掛載tmp text&#xff0c;在里面隨便放入非空格字符。 tmp text組件開放了textInfo接口&#xff0c;也就是GetComponent<TextMeshProUGUI>().textInfo…

兩天學會微服務網關Gateway-Gateway簡介

鋒哥原創的微服務網關Gateway視頻教程&#xff1a; Gateway微服務網關視頻教程&#xff08;無廢話版&#xff09;_嗶哩嗶哩_bilibiliGateway微服務網關視頻教程&#xff08;無廢話版&#xff09;共計17條視頻&#xff0c;包括&#xff1a;1_Gateway簡介、2_Gateway工作原理、3…