C#中欄位、屬性、唯讀、建構函式賦值、反射賦值的相關

來源:互聯網
上載者:User

標籤:變數   的區別   取值   語言   組合語言   OLE   實現   ace   .com   

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

C#中欄位、屬性、唯讀、建構函式賦值、反射賦值的相關

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.