C#中字段、屬性、只讀、構(gòu)造函數(shù)賦值、反射賦值的問題
C#中字段、屬性和構(gòu)造函數(shù)賦值的問題提出問題如下所述:
首先提出幾個問題:
1、如何實現(xiàn)自己的注入框架?
2、字段和自動屬性的區(qū)別是什么?
3、字段和自動屬性聲明時的直接賦值和構(gòu)造函數(shù)賦值有什么區(qū)別?
4、為什么只讀字段和只讀自動屬性(只有g(shù)et沒有set訪問器)都可以在構(gòu)造函數(shù)中進(jìn)行賦值?
5、反射可以給只讀字段或者只讀屬性進(jìn)行賦值嗎?
6、自動屬性和普通屬性的區(qū)別?
這些問題是我在試著寫自己的注入實現(xiàn)時遇到的問題。這些問題應(yīng)該在學(xué)習(xí)C#時的第一節(jié)課就應(yīng)該學(xué)到了,我看網(wǎng)上還有人分享說他在面試時遇到面試官問為什么只讀字段和只讀自動屬性可以在構(gòu)造函數(shù)中進(jìn)行賦值,他沒有回答上來,然后他寫文章探討這個問題,卻沒有得出一個明顯的答案,實在可惜。網(wǎng)上關(guān)于只讀屬性有些是寫ReadOnly特性的,讀到這些文章直接跳過吧,老版本的C#現(xiàn)在看也沒什么幫助。
給出答案
2、屬性比字段多了get/set訪問器;字段是在內(nèi)存中聲明的一個內(nèi)存空間,可以實實在在的存儲值;屬性像字段一樣使用,卻可以有自己的代碼段,能賦值取值,是因為訪問屬性就是調(diào)用屬性的get/set方法對字段進(jìn)行取值賦值(或者不操作字段);在MSDN上,建議字段作為類的私有變量使用private/protected修飾,屬性則往往作為共有屬性使用public修飾;字段的讀取和操作都是直接操作內(nèi)存,屬性是調(diào)用get/set訪問器,所以字段比屬性快。
3、準(zhǔn)確來說,沒有區(qū)別。區(qū)別僅僅是直接賦值先執(zhí)行,構(gòu)造函數(shù)賦值后執(zhí)行。在生成的IL中間語言(C#代碼先編譯成IL代碼,然后才編譯成匯編語言)中,字段直接賦值和構(gòu)造函數(shù)賦值是在同一個代碼段中(構(gòu)造函數(shù)中)的。
4、這個問題可以和上面的問題聯(lián)合起來回答。構(gòu)造函數(shù)作為實例化一個類的入口,是最先訪問的。字段的直接賦值其實也是放在構(gòu)造函數(shù)中執(zhí)行的,所以才說直接賦值和構(gòu)造函數(shù)賦值沒有區(qū)別?!爸蛔x”的限制只是由C#編譯器(CLR)維護(hù)的,我覺得全名應(yīng)該叫做“除構(gòu)造函數(shù)外只讀”更加準(zhǔn)確,這是C#語法的規(guī)則記住就行(這是當(dāng)然,直接賦值其實是放在構(gòu)造函數(shù)中進(jìn)行賦值的,如果構(gòu)造函數(shù)不能賦值那只讀字段沒有值和沒有聲明一樣);
5、這個問題又可以和上面的問題聯(lián)系起來一起回答。通過反射可以給自讀字段賦值但是無法給只讀屬性進(jìn)行賦值(不相信的可以試一下)。對只讀字段的賦值是因為繞過了C#編譯器(CLR)的只讀顯示,對只讀屬性賦值的話是還是調(diào)用set訪問器對字段進(jìn)行賦值,因為沒有set訪問器所以允許后會報錯。那么問題來了,那為什么只讀自動屬性沒有set訪問器還可以在構(gòu)造函數(shù)中賦值呢?其實只讀自動屬性在構(gòu)造函數(shù)中進(jìn)行賦值,實質(zhì)上是對字段進(jìn)行賦值,和屬性的get/set訪問器沒有關(guān)系。
6、區(qū)別是什么?上面一直強(qiáng)調(diào)自動屬性,是因為自動屬性和普通屬性不一樣,比如只讀普通屬性(沒有set訪問器)無法在構(gòu)造函數(shù)中賦值。在沒有自動屬性之前,普通屬性使用步驟是首先聲明一個字段如_id,然后聲明一個屬性Id,在get和set訪問器中做一些操作,這些操作大多數(shù)是對字段_id的操作,但是有時候和字段沒有關(guān)系。普通屬性可以像字段一樣通過“.”的方式調(diào)用,但又像方法一樣具有代碼段(普通屬性從來不開辟內(nèi)存空間)。
但是C#3.0之后引入了自動屬性,聲明方式如public int id { get; set; },C#6.0之后又有了public string FirstName { get; set; } = "Jane"。自動屬性肯定開辟了內(nèi)存空間然后才有了自動屬性的直接賦值。其實在類中聲明自動屬性會在編譯成IL中間語言中聲明一個隱藏字段,然后生成隱藏字段的get/set方法,然后生成get/set訪問器。這里可以解釋為什么只讀普通屬性無法在構(gòu)造函數(shù)中賦值(和直接賦值)而只讀自動屬性可以在構(gòu)造函數(shù)中賦值(和直接賦值),因為不論直接賦值還是在構(gòu)造函數(shù)中賦值,生成的IL代碼中的構(gòu)造函數(shù)中,操作的都是隱藏字段,并沒有訪問屬性的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的構(gòu)造函數(shù) .ctor,ctor是構(gòu)造函數(shù)(Constructor)。
不論直接賦值還是構(gòu)造函數(shù)賦值,都是在.ctor中執(zhí)行的,并且操作的都是字段,自動屬性的賦值操作的是隱藏字段。
public interface IUser { int id { get; set; } }
可以看到,接口中的自動屬性并沒有生成隱藏字段。
其他說明
1、上文中提到“反射可以給只讀字段進(jìn)行賦值但是無法給只讀屬性進(jìn)行賦值”。無法給只讀屬性進(jìn)行賦值是因為沒有set訪問器。但是我們已經(jīng)知道了可以給字段賦值,并且只讀屬性會生成隱藏字段,那我們是不是可以通過給隱藏字段進(jìn)行賦值間接達(dá)到給自動屬性賦值的目的呢?答案是可以的!
定義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、只讀自動屬性說明不想被訪問到那為什么還要給它賦值呢?這個問題……做著玩,項目中我覺得也沒有什么用到的機(jī)會……
總結(jié)
以上所述是小編給大家介紹的C#中字段、屬性、只讀、構(gòu)造函數(shù)賦值、反射賦值的問題 ,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復(fù)大家的!
- C# ODP.NET 調(diào)用Oracle函數(shù)返回值時報錯的一個解決方案
- C# 中如何取絕對值函數(shù)
- C#函數(shù)式編程中的惰性求值詳解
- asp.net(c#)獲取內(nèi)容第一張圖片地址的函數(shù)
- C#關(guān)于Task.Yeild()函數(shù)的討論
- C# 構(gòu)造函數(shù)如何調(diào)用虛方法
- 淺談C# 構(gòu)造方法(函數(shù))
- C#后臺調(diào)用前臺JS函數(shù)方法
- C#使用ILGenerator動態(tài)生成函數(shù)的簡單代碼
- C#中加載dll并調(diào)用其函數(shù)的實現(xiàn)方法
- 淺析C# 函數(shù)的傳值與傳址
相關(guān)文章
C#中IDispose接口的實現(xiàn)及為何這么實現(xiàn)詳解
這篇文章主要給大家介紹了關(guān)于C#中IDispose接口的實現(xiàn)及為何這么實現(xiàn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05C#列出當(dāng)前系統(tǒng)所有正在運行程序的方法
這篇文章主要介紹了C#列出當(dāng)前系統(tǒng)所有正在運行程序的方法,涉及C#操作系統(tǒng)進(jìn)程的技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-04-04C#實現(xiàn)十進(jìn)制數(shù)轉(zhuǎn)換為十六進(jìn)制的幾種方式
在C#中,十進(jìn)制和十六進(jìn)制轉(zhuǎn)換非常簡單,本文給大家介紹了C#實現(xiàn)十進(jìn)制數(shù)轉(zhuǎn)換為十六進(jìn)制的幾種方式,并通過代碼示例講解的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-04-04