C#中欄位、屬性、只讀、構造函數賦值、反射賦值的相關

来源:https://www.cnblogs.com/gdpw/archive/2018/08/12/9463145.html
-Advertisement-
Play Games

C#中欄位、屬性和構造函數賦值的問題 提出問題 首先提出幾個問題: 1、如何實現自己的註入框架? 2、欄位和自動屬性的區別是什麼? 3、欄位和自動屬性聲明時的直接賦值和構造函數賦值有什麼區別? 4、為什麼只讀欄位和只讀自動屬性(只有get沒有set訪問器)都可以在構造函數中進行賦值? 5、反射可以給 ...


C#中欄位、屬性和構造函數賦值的問題

提出問題

首先提出幾個問題:

1、如何實現自己的註入框架?

2、欄位和自動屬性的區別是什麼?

3、欄位和自動屬性聲明時的直接賦值和構造函數賦值有什麼區別?

4、為什麼只讀欄位和只讀自動屬性(只有get沒有set訪問器)都可以在構造函數中進行賦值?

5、反射可以給只讀欄位或者只讀屬性進行賦值嗎?

6、自動屬性和普通屬性的區別?

這些問題是我在試著寫自己的註入實現時遇到的問題。這些問題應該在學習C#時的第一節課就應該學到了,我看網上還有人分享說他在面試時遇到面試官問為什麼只讀欄位和只讀自動屬性可以在構造函數中進行賦值,他沒有回答上來,然後他寫文章探討這個問題,卻沒有得出一個明顯的答案,實在可惜。網上關於只讀屬性有些是寫ReadOnly特性的,讀到這些文章直接跳過吧,老版本的C#現在看也沒什麼幫助。

給出答案

2、屬性比欄位多了get/set訪問器;欄位是在記憶體中聲明的一個記憶體空間,可以實實在在的存儲值;屬性像欄位一樣使用,卻可以有自己的代碼段,能賦值取值,是因為訪問屬性就是調用屬性的get/set方法對欄位進行取值賦值(或者不操作欄位);在MSDN上,建議欄位作為類的私有變數使用private/protected修飾,屬性則往往作為共有屬性使用public修飾;欄位的讀取和操作都是直接操作記憶體,屬性是調用get/set訪問器,所以欄位比屬性快

3、準確來說,沒有區別。區別僅僅是直接賦值先執行,構造函數賦值後執行。在生成的IL中間語言(C#代碼先編譯成IL代碼,然後才編譯成彙編語言)中,欄位直接賦值和構造函數賦值是在同一個代碼段中(構造函數中)的。

4、這個問題可以和上面的問題聯合起來回答。構造函數作為實例化一個類的入口,是最先訪問的。欄位的直接賦值其實也是放在構造函數中執行的,所以才說直接賦值和構造函數賦值沒有區別。“只讀”的限制只是由C#編譯器(CLR)維護的,我覺得全名應該叫做“除構造函數外只讀”更加準確,這是C#語法的規則記住就行(這是當然,直接賦值其實是放在構造函數中進行賦值的,如果構造函數不能賦值那隻讀欄位沒有值和沒有聲明一樣);

5、這個問題又可以和上面的問題聯繫起來一起回答。通過反射可以給自讀欄位賦值但是無法給只讀屬性進行賦值(不相信的可以試一下)。對只讀欄位的賦值是因為繞過了C#編譯器(CLR)的只讀顯示,對只讀屬性賦值的話是還是調用set訪問器對欄位進行賦值,因為沒有set訪問器所以允許後會報錯。那麼問題來了,那為什麼只讀自動屬性沒有set訪問器還可以在構造函數中賦值呢?其實只讀自動屬性在構造函數中進行賦值,實質上是對欄位進行賦值,和屬性的get/set訪問器沒有關係。

6、區別是什麼?上面一直強調自動屬性,是因為自動屬性和普通屬性不一樣,比如只讀普通屬性(沒有set訪問器)無法在構造函數中賦值。在沒有自動屬性之前,普通屬性使用步驟是首先聲明一個欄位如_id,然後聲明一個屬性Id,在get和set訪問器中做一些操作,這些操作大多數是對欄位_id的操作,但是有時候和欄位沒有關係。普通屬性可以像欄位一樣通過“.”的方式調用,但又像方法一樣具有代碼段(普通屬性從來不開闢記憶體空間)。

但是C#3.0之後引入了自動屬性,聲明方式如public int id { get; set; },C#6.0之後又有了public string FirstName { get; set; } = "Jane"。自動屬性肯定開闢了記憶體空間然後才有了自動屬性的直接賦值。其實在類中聲明自動屬性會在編譯成IL中間語言中聲明一個隱藏欄位,然後生成隱藏欄位的get/set方法,然後生成get/set訪問器。這裡可以解釋為什麼只讀普通屬性無法在構造函數中賦值(和直接賦值)而只讀自動屬性可以在構造函數中賦值(和直接賦值),因為不論直接賦值還是在構造函數中賦值,生成的IL代碼中的構造函數中,操作的都是隱藏欄位,並沒有訪問屬性的set訪問器。(註意這裡只是說的類中的自動屬性,介面中也是可以有自動屬性的,但是介面的自動屬性並不會生成隱藏欄位只是定義get/set訪問器)

開始解釋

通過C#生成的IL中間語言代碼可以知道的更清楚

    public class User
    {
        public int id = 0;
        public int age { get; set; } = 1;
        public User()
        {
            id = 2;
            age = 3;
        }
    }

可以看到,自動屬性會生成一個名稱為 '<age>k__BackingField' 的隱藏私有欄位+私有欄位的get/set方法+屬性代碼段;

可以看到IL代碼生成了User的構造函數 .ctor,ctor是構造函數(Constructor)。

不論直接賦值還是構造函數賦值,都是在.ctor中執行的,並且操作的都是欄位,自動屬性的賦值操作的是隱藏欄位

  public interface IUser
  {
    int id { get; set; }
  }

可以看到,介面中的自動屬性並沒有生成隱藏欄位

其他說明

1、上文中提到“反射可以給只讀欄位進行賦值但是無法給只讀屬性進行賦值”。無法給只讀屬性進行賦值是因為沒有set訪問器。但是我們已經知道了可以給欄位賦值,並且只讀屬性會生成隱藏欄位,那我們是不是可以通過給隱藏欄位進行賦值間接達到給自動屬性賦值的目的呢?答案是可以的!

定義User的只讀自動屬性

    public class User
    {
        public int age { get;  } = 1;
        public User()
        {
            age = 3;
        }
    }

控制台的反射賦值代碼:

            var user = new User();
            try { typeof(User).GetProperty("age").SetValue(user, 9); }
            catch{    Console.WriteLine("只讀屬性賦值失敗");}
            typeof(User).GetField("<age>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(user,9);
            Console.WriteLine(user.age);
            Console.Read();

運行

2、因為隱藏欄位是私有的,所以取到隱藏欄位需要  BindingFlags.NonPublic

3、只讀自動屬性說明不想被訪問到那為什麼還要給它賦值呢?這個問題……做著玩,項目中我覺得也沒有什麼用到的機會……


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、數據結構 從不同的角度,可以分為邏輯結構和物理結構 邏輯結構:是數據元素之間的相互關係 集合結構 線性結構 樹形結構 圖形結構 物理結構:數據的邏輯結構在電腦的存儲形式 順序存儲結構:數據間的邏輯關係和物理關係一致 鏈式存儲結構 2、演算法時間複雜度 時間複雜度T(n)=O(f(n));f(n) ...
  • 由於需要在公司的內網進行神經網路建模試驗(https://www.cnblogs.com/NosenLiu/articles/9463886.html),為了更方便的在內網環境下快速的查閱資料,構建深度學習模型,我決定使用爬蟲來對深度學習框架keras的使用手冊進行爬取。 keras中文文檔的地址是 ...
  • 編程零基礎如何學習Python 如果你是零基礎,註意是零基礎,想入門編程的話,我推薦你學Python。雖然國內基本上是以C語言作為入門教學,但在麻省理工等國外大學都是以Python作為編程入門教學的。 那麼如何學習Python呢? 第一步:先把刀磨好 俗話說得好,磨刀不誤砍柴工,這個你不得不信,反正 ...
  • 在具備了volatile、CAS和模板方法設計模式的知識之後,我們可以來深入學習下AbstractQueuedSynchronizer(AQS),本文主要想從AQS的產生背景、設計和結構、源代碼實現及AQS應用這4個方面來學習下AQS,文章耗時一個月,所以篇幅有點長,需要一點耐心。 1、AQS產生背 ...
  • Size Balanced Tree 挺有意思的, 適合新手練習 ...
  • 事件是C#的基礎之一,學好事件對於瞭解.NET框架大有好處。 事件最常見的比喻就是訂閱,即,如果你訂閱了我的博客,那麼,當我發佈新博客的時候,你就會得到通知。 而這個過程就是事件,或者說是事件運行的軌跡。 事件是發散,以我的博客為核心,向所有訂閱者發送消息。我們把這種發散稱之為[多播]。 最常見的事 ...
  • 0.簡介 Abp 本身集成了一套許可權驗證體系,通過 ASP.NET Core 的過濾器與 Castle 的攔截器進行攔截請求,併進行許可權驗證。在 Abp 框架內部,許可權分為兩塊,一個是功能(Feature),一個是許可權項(Permission),在更多的時候兩者僅僅是概念不同而已,大體處理流程還是一 ...
  • v4.2.1 更新內容:1.重新定義數據轉發文本協議,使網關與ServerSuperIO以及之間能夠相關交互數據。2.擴展ServerSuperIO動態數據類的方法,更靈活。3.修複Designer增加轉發任務的一個BUG。4.修改數據轉發客戶端和服務端。5.增加硬體網關驅動。 v4.2.1 下載地 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...