4年前我來到現在的公司服務,那時我不知道這家公司是做什麼的,面試的時候是考英文跟申論題,那些申論題完全跟C#沒什麼關係,倒是跟開發有關,考完後跟主管面談時,主管也沒有談很多C#的細節,有稍微問一下我開發C#的經驗,那時我對C#的開發僅是玩票性質的而已,輕描淡寫的說了Google Blogger版的誰來我家,就過關跟副總面試了。我想我是幸運的,所以面試的時候沒有被刁難C#的問題,但是下一次可能就不是這樣了,所以還是先懂一下,要找C#相關工作的朋友最好先來看看有可能被考到的C#問題。
這裡有50個 C# 面試時最常被問到的問題及答案,這些問題有些適用於初學者,有些則適用於專業的C#開發人員。
1. 什麼是 C#?
C# 是一種電腦程式語言,微軟在2000年發布了這種語言,旨在提供一種現代通用的程式語言,用來開發所有種類的軟體在不同的平台上包括 Windows、Web及手機上,就只要使用這一個程式語言即可,今天C#是世上最受歡迎的語言之一,數百萬的軟體開發人員使用C#來建置所有種類的軟體。
C# 是建置微軟.NET軟體應用程式最主要的語言,開發者使用C#幾乎可以建置每一種的軟體包括Windows UI 應用程式、控制台應用程式、後端服務、雲端API、Web服務、控制項及函式庫、無伺服器應用程式、Web應用程式、原生iOS跟Android應用程式、AI 跟機器學習軟體以及區塊鏈應用程式。
C# 因為Visual Studio IDE的幫助提供了快速的應用程式開發,C# 是現代、物件導向、簡單、通用及績效導向的程式語言,C# 參考了幾種程式語言的最佳功能及使用案例來開發,這些語言包括 C++、Java、Pascal及SmallTalk。
C# 語言很像C++ .NET 而C#函式庫很像Java,C# 支援現代物件導向的程式語言功能包括抽象(Abstraction)、封裝(Encapsulation)、多型(Polymorphism)和繼承(Inheritance),C#是強型別的語言,大部分的型別繼承自物件類別。
C# 支援類別跟物件的概念,類別有成員像是欄位field、屬性property、事件event、跟方法method,詳細的資料可以看一下C# 跟 OOP 的文章。
C# 是通用的、現代的及支援現代程式語言的需求,自成立以來,C# 語言經歷了很多改版,最新的版本是v8.0,有關C# 8的更多資料,可以參考 Learn C# 8.0 Step by Step。
2. C#的物件是什麼?
C# 語言是一種物件導向的程式語言,類別是其基礎,類別是一種定義資料結構的外觀及資料如何儲存、管理及傳遞的樣板,類別有欄位、屬性、方法跟其他成員。
Class是一種概念的感覺,而物件是實體的,物件使用類別的實體來產生,類別定義物件的型別,物件在電腦記憶體中儲存真實的值。
真實世界中都會有一些特徵或這些特徵可以執行某些工作的實體,這個實體被稱作物件,物件也被稱為實體,例如程式語言中實體的複製,物件是類別的實體。
舉例來說,我們需要建置一支程式來處理汽車,我們需要產生汽車的實體,稱作Car類別,汽車有4個屬性,model型號、type類型、color顏色及size大小。
為了在程式中表示汽車,我們可以建一個類別 Car有4個屬性,Model、Type、Color跟Size,這些成為類別的成員,類別會有幾種型別:成員、建構子、欄位、屬性、方法、委派跟事件,類別成員可以是私有的、保護的跟公有的,屬性可以在類別外被存取的屬性,就是公有的屬性。
物件是類別的實體,類別需要的話會有很多實體,例如,Honda Civic是汽車的實體,在程式中Honda Civic是一個物件,Honda Civic是類別Car的實體,Model、Type、Color及Size是屬性,Honda Civic的這些屬性分別是Civic、Honda、Red跟 4,BMW 330、Toyota Carolla、Ford 350、Honda CR4、Honda Accord 跟Honda Pilot都是汽車物件的例子。
有關物件導向的更多觀念,可以閱讀深入淺出物件導向分析與設計這本書。
3. 什麼是託管程式碼Managed Code 或非託管程式碼 Unmanaged Code?
託管程式碼 Managed Code
“託管程式碼是使用.NET framework及其支援的程式語言像是C# 或是VB.NET開發的程式碼,託管程式碼由通用語言執行平台(Common Language Runtime,簡稱CLR)直接執行,其生命週期包括物件建置、記憶體配置及物件回收在執行時管理,在.NET Framework寫的語言都是託管程式碼”。
非託管程式碼Unmanaged Code
在.NET framework外開發的程式碼就是非託管程式碼。
“應用程式不需要在CLR控制下執行就可以說是非託管的,像 C、C++或是Visual Basic都是非託管的。
非託管程式碼的物件建置、執行及回收都直接由程式設計師來管理,如果程式設計師寫錯程式碼,就會導致記憶體洩漏及不必要的資源配置”。
.NET Framework提供了一種機制可以在託管程式碼中使用非託管程式碼,或是非託管的程式碼使用託管的程式碼,這個過程可以透過包裝類別的幫助來完成。
4. 什麼是封箱 Boxing與拆箱 Unboxing?
封箱與拆箱用於行別的轉換。
將數值型別轉換到參考行別的過程叫做封箱,封箱是一種隱含轉換,封箱的例子如下:
// Boxing int anum = 123; Object obj = anum; Console.WriteLine(anum); Console.WriteLine(obj);
將參考型別轉換成數值型別的過程叫做拆箱,拆箱的例子如下:
// Unboxing Object obj2 = 123; int anum2 = (int)obj; Console.WriteLine(anum2); Console.WriteLine(obj);
5. C# 結構跟類別的差異?
類別和結構都是使用者定義的資料型別,主要的差異如下:
Struct
- C# 的結構是數值型別,繼承自 System.Value 型別。
- 結構用通常於小量資料。
- 結構不能繼承自其他的型別。
- 結構不能是抽象的。
- 無須使用new關鍵自來產生物件。
- 沒有建置預設建構子的權限。
Class
- 類別是C# 的參考型別,繼承自 System.Object 型別。
- 類別通常用於大量的資料。
- 類別可以繼承自其他類別。
- 類別可以是抽象型別。
- 可以建立預設的建構子。
6. C# 介面跟抽象類別的差異?
介面跟抽象有一些常見的差異:
- 一個類別可以實做任意數量的介面而一個子類別最多只能使用一個抽象類別。
- 抽象類別可以有非抽象方 (concrete methods具體方法)而使用介面的情況下,所有的方法必須是抽象的。
- 抽象類別可以宣告或使用任何變數,而介面不允許這樣做。
- 在抽象類別中,所有的資料成員或是方法預設是私有的,而介面裡的資料成員及方法預設是公有的,我們不能手動改變他們。
- 在抽象類別中我們需要使用abstract關鍵自來宣告抽象方法,而在介面中我們不需要這樣做。.
- 抽象類別不能用於多重繼承,而介面可以用於多重繼承。
- 抽象類別使用建構子而在介面裡沒有任何型別的建構子。
7. C# 的列舉是什麼?
列舉是一種數值型別具有一組相關的具名常數,通常稱為列舉清單,enum 關鍵字用來宣告列舉,這是使用者定義的基本資料型別。
列舉型別可以是 integer (float, int, byte, double…等等),但是如果在int旁邊使用必須強制轉換。
列舉用在 .NET framework建立數值常數,所有列舉的成員都是列舉型別。每一個列舉型別都必須是數值。
列舉元素預設的基礎型別是int,預設第一個列舉值是0,每個連續的列舉值會以1增加。
enum Dow {Sat, Sun, Mon, Tue, Wed, Thu, Fri};
關於列舉的一些要點:
- 列舉在C#中是列舉的資料型別。
- 列舉不是給最終使用者用的,主要是針對開發者使用的。
- 列舉是強型別常數,例如,一個型別的列舉不可以隱式分配至另一型別的列舉縱使他們成員的基礎值一樣也不行。
- 列舉可以讓程式碼容易閱讀及了解。
- 列舉值是固定的,列舉用字串顯示用整數值來處理。
- 預設型別是int,核准的型別有byte、sbyte、short、ushort、uint、long及ulong。
- 每個列舉型別由System.Enum自動衍生而來,因此可以使用System.Enum方法。
- 列舉是數值型別建立在堆疊(Stack)上而非堆積(Heap)上。
8. “continue”與“break”陳述的差異有哪些?
使用break 陳述你可以跳出迴圈,而使用continue 陳述你可以跳過一次迭代 iteration,然後繼續執行你的迴圈。
Break 陳述範例
using System; using System.Collections; using System.Linq; using System.Text; namespace break_example { Class brk_stmt { public static void main(String[] args) { for (int i = 0; i <= 5; i++) { if (i == 4) { break; } Console.WriteLine("The number is " + i); Console.ReadLine(); } } } }
輸出
The number is 0;
The number is 1;
The number is 2;
The number is 3;
Continue 陳述範例
using System; using System.Collections; using System.Linq; using System.Text; namespace continue_example { Class cntnu_stmt { public static void main(String[] { for (int i = 0; i <= 5; i++) { if (i == 4) { continue; } Console.WriteLine(“The number is "+ i); Console.ReadLine(); } } } }
輸出
The number is 1;
The number is 2;
The number is 3;
The number is 5;
有關相關的跳躍陳述式,可以參考陳述式關鍵字 (C# 參考)。
9. constant 跟 readonly的差異是?
Const 就只是常數而已,這是一個在編譯時期值是固定的變數,且必須有值,預設情況下,常數是靜態的在程式執行時不能改變常數變數的值。
Readonly是在執行階段期可以變更值的關鍵字,而且只能透過非靜態建構函式來指定。
範例
有一個Test類別有兩個變數,一個是readonly,另一個是常數。
class Test { readonly int read = 10; const int cons = 10; public Test() { read = 100; cons = 100; } public void Check() { Console.WriteLine("Read only : {0}", read); Console.WriteLine("const : {0}", cons); } }
在這裡,我試著在建構函式中修改變數值,但是當我試著變更常數,會出現我必須在執行階段才能變更值的錯誤區塊。
最後,移除那一行程式碼,呼叫Check()函式就像下面的這一段程式碼:
class Program { static void Main(string[] args) { Test obj = new Test(); obj.Check(); Console.ReadLine(); } } class Test { readonly int read = 10; const int cons = 10; public Test() { read = 100; } public void Check() { Console.WriteLine("Read only : {0}", read); Console.WriteLine("const : {0}", cons); } }
輸出
10. ref 跟 out 關鍵字的差異是?
ref 關鍵字是以傳址方式傳遞參數,也就是當控制項傳回呼叫的方法時在這個方法中這個參數的任何改變都將反應在這個變數裡。
out 關鍵字也是以傳址方式傳遞參數,跟ref關鍵字很類似。
Ref | Out |
參數在被傳到ref前必須先被初始化 | 在傳到out前參數不須先初始化 |
在傳回呼叫的方法前不需要指定或是初始化參數值(這是用傳址方式傳遞) | 在傳回呼叫的方法前呼叫的方法需要指定或初始化參數值(這是參數傳到out) |
當呼叫的函式也需要修改傳遞的參數時,透過傳址方式傳遞參數很有用 | 當有多重值需要從一個含式或方法傳回時宣告一個參數給out的方法是有用的 |
在呼叫方法裡在使用前不須初始化參數值 | 在呼叫方法裡使用前參數值必須初始化 |
在使用傳址方式傳遞參數時,資料可以雙向傳遞 | 在使用OUT時資料只能以單向方式來傳遞(從被呼叫的方法到呼叫的方法) |
ref跟out在執行階段的處理是不同的,但是在編譯時期的處理是一致的。 | |
屬性不是變數,因此你不能將它做為out或是ref的參數來傳遞。 |
11. “this”可以用在靜態方法中嗎?
在靜態方法中不可以使用’this’,因為關鍵字’this’會傳回包含它的類別目前的實體,靜態方法(或是任何靜態成員)不屬於一個特定的實體,他們存在時沒有建置類別的實體,是用類別的名稱被呼叫,而不是透過實體來呼叫,所以我們不能在靜態方法中使用這個關鍵字,然而,在Extension方法中我們可以使用這含式的參數。
在C#中”this” 關鍵字是參考變數的一個特殊型別,它隱含定義在每個建構子及非靜態方法中,做為該型別類別第一個被定義的參數。
12. 什麼是C# 的屬性?
C# 屬性是C# 類別的成員,提供彈性的機制來讀寫或是計算私有欄位的值,換句話說,使用屬性可以存取私有欄位並設定他們的值,屬性在C# 中都是公用資料成員,C# 屬性使用get 跟set 方法(也稱為存取子),來存取及指派值給私有欄位。
什麼是存取子?
屬性的取得和設定部份或是區塊稱作存取子,對限制屬性的存取很有用,設定的存取子指定我們可以在屬性中指派值給私有欄位,沒有設定存取子的屬性,它就像唯讀欄位,有了’get’ 存取子我們就可以存取私有欄位的值,換句話說,它傳回單個值,get存取子指定我們可以公開地存取欄位值。
有三種屬性型態:Read/Write, ReadOnly, 跟 WriteOnly。
13. c# 的extension方法是什麼?
擴充方法允許你新增方法到現有的型別而不用新增新的衍生型別、重新編譯、或是修改原始型別。
擴充方法是一種特別的靜態方法,他們的呼叫就像擴充型別上實體方法那樣。
如何使用擴充方法?
擴充方法是靜態類別的靜態方法,”this”修飾詞會作為第一個參數來用,第一個參數的型別就會是擴充的型別。
您必須使用 using 指示詞將命名空間明確匯入至原始程式碼,擴充方法才會進入範圍中。
14. C#的dispose和finalize方法的差異是?
finalize跟dispose都是用來釋放非託管資源的方法。
Finalize
- Finalize用來釋放沒有使用的非託管資源,像是在應用程式定義域裡的檔案、資料庫連線…等等,這些是物件在被銷毀前物件所佔用的資源。
- 內部程式中只能透過記憶體回收(Garbage Collector)來呼叫,不能由使用者程式碼或服務來手動呼叫。
- Finalize屬System.Object類別。
- 你的程式碼有非託管的資源時就可以執行,確定記憶體回收時這些資源會被釋放。
Dispose
- Dispose用來釋放沒有使用的非託管資源,像是任何時間應用程式定義域裡的檔案、資料庫連線等等。
- Dispose很明顯地是由使用者的程式碼來手動呼叫。
- 假如我們需要使用dispose方法,我們必須透過IDisposable介面來執行這個類別。
- Dispose屬於IDisposable介面。
- 當你在寫一個特製的類別會由其他使用者來使用時,就可以執行這個方法。
15. C#的String和StringBuilder有什麼不同?
StringBuilder和string都是用於字串值,但兩者在實體的建立及效能上有很多的不同。
String
string是不可變物件,不可變是當我們在程式碼中建立string物件,我們在任何的操作中像是新增新的值、取代或是新增任何值在string物件中現有值中,都不能修改或改變物件,當我們必須做一些處理來改變string,就只有消除string物件的舊有值,再新增一個新的實體在記憶體中來保有一個字串物件新的值,例如:
注意
- 它是保有字串值的不可變物件。
- 從性能角度來看,字串速度很慢,因為它會建立一個新的實體來覆蓋或更改先前的值。
- String屬於System命名空間。
StringBuilder
System.Text.Stringbuilder是一種可以保有字串值的可變物件,可變的意思是一旦我們建立一個System.Text.Stringbuilder物件,我們可以使用這個物件做任何操作像是使用新增函式在現有的字串新增值,也可以不用每次都要建立新的System.Text.Stringbuilder實體來取代或是增加字串,所以他是一直使用之前的物件,這樣的話,比起System.String會比較快,可以看看下面的範例來了解 System.Text.Stringbuilder。
注意
- StringBuilder是可變物件。
- 從性能角度來看StringBuilder會很快是因為它會使用同一個StringBuilder物件的實體來執行任何操作,像是新增一個值在現有的字串上。
- StringBuilder屬於System.Text.Stringbuilder命名空間。
16. C#的委派是什麼,以及如何使用?
委派是一個以上函式指標的抽象(這在C++裡面有,有關其說明不在本章討論範圍中),.NET用委派的形式來實作函式指標的概念。有了委派,可以像資料一樣來處理函式,委派允許函式可以有參數傳遞,從函式傳回值並儲存在陣列中,委派有下列幾個特色:
- 委派衍生自System.MulticastDelegate類別。
- 他們有一個signature跟return型別,增加到委派的函式必須跟這個signature相容。
- 委派可以指向靜態或是實體的方法。
- 一旦委派的物件建立,就可以在執行時期動態叫用(invoke)它指向的方法。
- 委派可以同步及非同步地呼叫方法。
委派包含很多有用的欄位,第一個保有物件的參考,第二個保有方法指標,當你叫用委派,實體方法會在持有的參考上被呼叫,但是如果物件的參考是空的那麼執行時就會被理解為靜態方法,而且句法上叫用委派就跟呼叫一般的函式完全一樣,因此委派很適合來實作回呼。
為什麼需要委派?
從歷史上看,Windows API常常使用C樣式的函式指標來建立回呼函式,使用回呼程式設計師能夠設定函式來回報給應用程式中的另一個函式,所以使用回呼就是處理按鈕點擊、選單選擇及滑鼠移動等活動,但是這個傳統方法的問題就是回呼函式並非型別安全的,在.NET framework裡,回呼還是能夠以更有效率的方法來使用委派,委派有三個重要的部份:
- 方法的參數
- 它呼叫方法的位址
- 方法傳回的型別
對於想要傳遞方法到另一個方法的情況委派是一種解決方案,你已經習慣於將資料作為參數傳遞給方法,以至於將方法作為參數而不是資料來傳遞的想法聽起來有些奇怪,但是,在某些情況下,你有一種方法可以執行某些操作,例如,調用其他方法,你不知道在編譯時第二種方法是什麼,那資訊僅在執行時期時可用,因此委派是克服這類複雜問題的方法。
17. C#的密封類別是什麼?
密封類別用來限制物件導向語言的繼承功能,一旦類別被定義為密封類別,這類別就不能被繼承。
在C#裡sealed修飾詞用來定義類別是密封的,在Visual Basic .NET裡是用Not Inheritable關鍵字來做密封類別,如果類別衍生自密封類別,編譯器會丟出錯誤。
如果你曾有留意過,結構也是密封的,所以也不能從結構衍生類別。
下面的類別定義定義了一個密封類別:
// Sealed class sealed class SealedClass { }
18. 什麼是部份類別?
部份類別只用於分離類別的定義為兩個或以上的類別在相同的原始碼檔案或是在兩個以上的原始檔案中,你可以在很多檔案中建構一個類別定義,但是它在執行時期會被編譯成一個類別,另外當你建構這個類別的實體時,你可以使用相同的物件從所有的原始檔案中存取所有的方法。
部份類別被建構在相同的命名空間中,在不同的命名空間中不能建構部份類別,所以使用“partial”關鍵字與要繫結在一起的所有類別名稱一起使用,並在同一命名空間中使用相同的類別名稱,讓我們來看一個例子:
19. C#的裝箱與拆箱是什麼?
裝箱與拆箱都用於型別的轉換,但是有一些區別:
裝箱 Boxing
裝箱是轉換數值型別資料型別為物件或是這個數值型別所實作的介面資料型別的過程,當CLR(共通語言執行環境)裝箱一個數值意思是當CLR轉換一個數值型別到物件型別時,它會包覆數值進System.Object,儲存在應用程式領域的heap(堆積)區域中。
範例
拆箱 Unboxing
拆箱也是一個過程用來從物件或是任何實作的介面型別取出數值型別,裝箱可以隱式轉換,但是拆箱不行。
範例:
裝箱與拆箱的概念對C#統一型別系統的觀點很重要,這是將任何型別的值當作物件來處理的觀點。
20. 什麼是C#的IEnumerable<> ?
列舉介面(IEnumerable)是 System.Collections命名空間中所有非泛型集合的父介面像是 ArrayList、HastTable等等可以列舉的類別,對於這個介面的泛型版本像是IEnumerable<T>是 System.Collections.Generic namespace命名空間中所有泛型集合類別的父介面,像是 List<>等等。
在System.Collections.Generic.IEnumerable<T>裡只有一個方法就是 GetEnumerator(),它會傳回 IEnumerator,IEnumerator透過公開 Current屬性、MoveNext及 Reset方法提供了逐一查看集合的能力,如果我們沒有這個介面作為父介面就不能透過 foreach迴圈來使用迭代,也不能在 LINQ查詢來使用類別物件。
21. C#的晚期繫結跟早期細節有什麼不同?
早期繫結跟晚期繫結是C#多型的概念,多型是物件導向語言的功能,它允許一種語言以不同的形式來使用相同的名稱,例如,一個叫 Add的方法可以新增整數、雙精確度浮點數(double)跟浮點數(decimal)。
多型有兩種不同的類型可以完成:
- 編譯時期也稱為早期繫結或是多載
- 執行時期也稱為晚期繫結或是覆寫
編譯時期多型或是早期繫結
在編譯時期的多型或是早期繫結,我們會使用相同名稱但是會有不同型別的參數、或可能是不同數目參數的多重方法,因為是這樣我們可以在相同的類別裡使用相同的方法名稱執行不同的任務,這也稱為方法多載。
下面的範例可以看我們如何使用:
執行時期多型或是晚期繫結
執行時期多型也稱為晚期繫結,在執行時期多型或是晚期繫結裡,我們可以使用有相同簽章的相同方法,這意思是有相同型別或是數目參數,但不可以在相同的類別裡有這樣的多型,因為編譯器不允許在編譯時期這樣做,因此,我們可以在子類別或是衍生類別物件將具現化的時候在執行時期的衍生類別裡使用那繫結,那就是為何會稱為晚期繫結的原因,我們必須使用override關鍵字將父類別函式建構為部份函式以及在驅動或是子類別中建構覆寫函式。
範例
22. IEnumerable跟 IQueryable的差異?
在我們討論差異前,讓我們了解 IEnumerable跟 IQueryable是什麼。
IEnumerable
是 System.Collections命名空間中(例如ArrayList、 HastTable等等)所有非泛型集合的父介面,可以對其進行列舉,這個介面的泛型版本是 IEnumerable<T>,這是System.Collections.Generic命名空間中所有泛型集合的父介面,像是 List<> 等等。
IQueryable
根據 MSDN,IQueryable介面是供查詢提供者使用,它僅應由也只有實作 IQueryable<T>的提供者來實作,如果提供者也沒有實作 IQueryable<T>,則不能在提供者的資料來源上使用標準的查詢運算子。
IQueryable介面繼承了 IEnumerable介面所以如果它表示查詢,則可以列舉查詢的結果,列舉會產生與 IQueryable相關聯的運算式樹狀架構被執行,”執行運算式樹狀架構”是查詢提供者所特有,例如,它可能涉及將運算式樹狀架構轉換為基礎資料來源的適當查詢語言,當 Execute方法被呼叫時會執行不回傳列舉結果的查詢。
IEnumerable |
IQueryable |
IEnumerable屬於 System.Collections命名空間 |
IQueryable屬於System.Linq命名空間 |
IEnumerable是把查詢寫在List、Array等集合資料行別的最好方法 |
IQueryable是寫查詢資料像是遠端資料庫、服務集合等的最好方法 |
IEnumerable是LINQ到物件以及LINQ到XML查詢的傳回型別 |
IQueryable是LINQ到SQL查詢的傳回型別 |
IEnumerable不支援延遲載入,所以對於分頁種類的情境來說是不推薦的方法。 |
IQueryable支援延遲載入,所以我們也可以使用分頁種類的情境 |
IEnumerable支援擴充(Extension)方法來取得LINQ查詢的功能化物件 |
IQueryable間接地實作IEnumerable,所以也支援擴充方法 |
23. 如果繼承的介面方法名稱衝突該怎麼辦?
如果我們在相同的類別實作多個介面有衝突的方法名稱,則不須定義全部,換句話說,我們可以說如果我們在相同的類別有衝突的方法,由於相同的名稱及簽章我們不可以在相同的類別中獨立實作它們的主體,因此我們必須在方法名稱前使用介面的名稱來刪除此方法的沒收,下面是範例:
interface testInterface1 { void Show(); } interface testInterface2 { void Show(); } class Abc: testInterface1, testInterface2 { void testInterface1.Show() { Console.WriteLine("For testInterface1 !!"); } void testInterface2.Show() { Console.WriteLine("For testInterface2 !!"); } }
現在看看如何在類別中使用這些介面:
class Program { static void Main(string[] args) { testInterface1 obj1 = new Abc(); testInterface1 obj2 = new Abc(); obj1.Show(); obj2.Show(); Console.ReadLine(); } }
輸出
24.C#的陣列是什麼?
在 C#裡,陣列索引從 0開始,這意味著陣列的第一個項目從第0個位置開始,陣列的最後一個項目位置是項目數 -1,所以如果陣列有 10個項目,最後第 10個項目是在第 9位置。
在 C#裡,陣列可以宣告成固定長度或是動態的。
固定長度的陣列儲存預先定義的項目數。
動態陣列沒有預先定義的大小,動態陣列的大小會隨你增加新的項目到陣列而增加,你可以宣告陣列是固定長度或是動態的,你甚至可以在定義後將動態陣列改為固定的陣列。
現在看看 C#陣列宣告的範例,下面的程式碼片段定義了最簡單的整數型別動態陣列。
int[] intArray;
你可以看到上面那段程式碼,陣列的宣告以陣列的型別開始,之後有方括弧 ([])跟陣列名稱。
下面的程式碼片段宣告一個儲存5個項目的陣列從索引0到4。
int[] intArray; intArray = new int[5];
下面的程式碼片段宣告儲存100個的陣列項目從索引0到99。
int[] intArray; intArray = new int[100];
25. C#的建構函式鏈是什麼?
建構函式鏈是一種將兩個或多個以上的類別以繼承的關係來連結,在建構函式鏈裡,每個子類別建構子透過 base關鍵字隱式映射到父類別的建構子,因此當你建置子類別的實體時,它會呼叫父類別的建構子,沒有這個,不能繼承。
26. Array.CopyTo() 跟 Array.Clone()有什麼不同?
Array.Clone()方法會建構陣列的淺層複製,陣列的淺層複製只會複製陣列的元素,不論他們是參考型別或是數值型別,但是不會複製參考所指向的物件,新陣列的參考指向原來陣列參考所指向的相同物件。
陣列類別的Copy()靜態方法會複製一部分的陣列到另一個陣列,CopyTo方法複製陣列的所有元素到另一個一維陣列,列表9中的程式碼會複製整數陣列的內容到物件行別的陣列中。
// Creates and initializes a new Array of type Int32. Array oddArray = Array.CreateInstance(Type.GetType("System.Int32"), 5); oddArray.SetValue(1, 0); oddArray.SetValue(3, 1); oddArray.SetValue(5, 2); oddArray.SetValue(7, 3); oddArray.SetValue(9, 4); // Creates and initializes a new Array of type Object. Array objArray = Array.CreateInstance(Type.GetType("System.Object"), 5); Array.Copy(oddArray, oddArray.GetLowerBound(0), objArray, objArray.GetLowerBound(0), 4); int items1 = objArray.GetUpperBound(0); for (int i = 0; i < items1; i++) Console.WriteLine(objArray.GetValue(i).ToString());
列表 9
27. 可以在 C#中執行多個 catch區塊嗎?
我們可以使用多個 catch區塊與 try敘述一起使用,每個 catch區塊可以捕獲不同的異常,下面的程式碼顯示多個catch敘述如何與單一的try敘述一起實作。
using System; class MyClient { public static void Main() { int x = 0; int div = 0; try { div = 100 / x; Console.WriteLine("Not executed line"); } catch (DivideByZeroException de) { Console.WriteLine("DivideByZeroException"); } catch (Exception ee) { Console.WriteLine("Exception"); } finally { Console.WriteLine("Finally Block"); } Console.WriteLine("Result is {0}", div); } }
28. 單例設計模式(Singleton Design Patterns)是什麼以及如何實作?
什麼是單例設計模式(Singleton Design Pattern)?
- 確保一個類只有一個實體,並提供對其存取全域的觀點。
- Singleton是一個類別僅允許其單一實體被建置,通常會對該實體提供簡單的存取。
- 最常見的是,單例在建置實體時不允許任何參數被指定,因為如果有不同參數的實體第二次的請求是會有錯誤的!(如果是對所有請求有相同參數的同一個實體應該被存取,那麼使用工廠模式會更適合)。
- 在 C#裡有一些方法來實作單例模式。
-
- 單一建構子,私有且無參數。
- 類別是密封的。
- 一個靜態變數其中包含對單一建置實體的參考(如果有)。
- 公共靜態方法是取得單一建置實體的參考,如果需要就建置一個。
如何使用單例來設計程式碼;
namespace Singleton { class Program { static void Main(string[] args) { Calculate.Instance.ValueOne = 10.5; Calculate.Instance.ValueTwo = 5.5; Console.WriteLine("Addition : " + Calculate.Instance.Addition()); Console.WriteLine("Subtraction : " + Calculate.Instance.Subtraction()); Console.WriteLine("Multiplication : " + Calculate.Instance.Multiplication()); Console.WriteLine("Division : " + Calculate.Instance.Division()); Console.WriteLine("n----------------------n"); Calculate.Instance.ValueTwo = 10.5; Console.WriteLine("Addition : " + Calculate.Instance.Addition()); Console.WriteLine("Subtraction : " + Calculate.Instance.Subtraction()); Console.WriteLine("Multiplication : " + Calculate.Instance.Multiplication()); Console.WriteLine("Division : " + Calculate.Instance.Division()); Console.ReadLine(); } } public sealed class Calculate { private Calculate() {} private static Calculate instance = null; public static Calculate Instance { get { if (instance == null) { instance = new Calculate(); } return instance; } } public double ValueOne { get; set; } public double ValueTwo { get; set; } public double Addition() { return ValueOne + ValueTwo; } public double Subtraction() { return ValueOne - ValueTwo; } public double Multiplication() { return ValueOne * ValueTwo; } public double Division() { return ValueOne / ValueTwo; } } }
29. 拋出例外跟子句有什麼不同
拋出例外基本的差異是他們覆寫堆疊追蹤(stack trace)不同,在有拋出異常時這很難發現原始的程式碼行號。
Throw 基本上保留堆疊資訊並在拋出異常時加進堆疊的資訊中。
讓我們看看這意義來更了解其差異,我使用控制台應用程式來簡單測試一下,並看看這兩者的用法在功能上有何不同。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestingThrowExceptions { class Program { public void ExceptionMethod() { throw new Exception("Original Exception occurred in ExceptionMethod"); } static void Main(string[] args) { Program p = new Program(); try { p.ExceptionMethod(); } catch (Exception ex) { throw ex; } } } }
現在按下F5來執行程式碼,看看發生什麼事,它會傳回例外,看看堆疊追蹤。
30. C#的索引子是什麼?
C# 引入了一個稱為索引子的新概念,用於將物件視為陣列,索引子在C#裡也稱為智能陣列,他們不是物件導向語言必要的部份。
定義索引子讓你可以像虛擬陣列那樣來建置類別,類別的實體使用 []陣列存取操作子來存取。
建置索引子
< modifier > < return type > this[argument list] { get { // your get block code } set { // your set block code } }
上面的程式碼中
<modifier>
可以是private、public、protected或 internal。
<return type>
可以是任何有效的 C#型別。
31. 多點傳送委派是什麼?
委派是.NET裡的基礎型別之一,委派是一個類別在執行時期用來建置及調用委派。
C#中的委派允許開發人員將方法視為物件,並從程式碼中來調用他們。
實作多點傳送委派的例子:
using System; using System.Collections.Generic; using System.Linq; using System.Text; delegate void MDelegate(); class DM { static public void Display() { Console.WriteLine("Meerut"); } static public void print() { Console.WriteLine("Roorkee"); } } class MTest { public static void Main() { MDelegate m1 = new MDelegate(DM.Display); MDelegate m2 = new MDelegate(DM.print); MDelegate m3 = m1 + m2; MDelegate m4 = m2 + m1; MDelegate m5 = m3 - m2; m3(); m4(); m5(); } }
32. 等號比較運算子(==)跟 Equals()方法的差異
== 運算子跟 Equals()方法都是用來比較兩個數值型別資料項目或是參考型別的資料項目,等號比較運算子(==)是比較運算子而 Equals()方法比較字串的內容,== 運算子比較參考識別而 Equals()方法只比較內容,讓我們看看例子。
在這個範例裡,我們指定一個字串值給另一個變數,字串是參考型別,在接下的例子裡,字串變數被指定給另一個字串變數所以他們會參考堆積(heap)中的相同識別,會有相同的內容,所以 ==運算子跟 Equals()方法都會得到True的輸出。
using System; namespace ComparisionExample { class Program { static void Main(string[] args) { string name = "sandeep"; string myName = name; Console.WriteLine("== operator result is {0}", name == myName); Console.WriteLine("Equals method result is {0}", name.Equals(myName)); Console.ReadKey(); } } }
33. is跟 as運算子的差異
“is” 運算子
C#語言中,我們使用 “is”運算子來檢查物件型別,如果兩個物件屬於同一型別,就傳回 true,否則就傳回 false。
讓我們看看程式碼來了解這個,我們宣告兩個類別,Speaker跟 Author。
class Speaker { public string Name { get; set; } } class Author { public string Name { get; set; } }
現在建置 Speaker型別的物件:
var speaker = new Speaker { Name="Gaurav Kumar Arora"};
檢查物件是否是 Speaker型別:
var isTrue = speaker is Speaker;
在前面我們檢查行別的匹配,是的 speaker是 Speaker型別的一個物件。
Console.WriteLine("speaker is of Speaker type:{0}", isTrue);
所以結果是 true。
但是下面的我們的結果是 false:
var author = new Author { Name = "Gaurav Kumar Arora" }; var isTrue = speaker is Author; Console.WriteLine("speaker is of Author type:{0}", isTrue);
因為 speaker不是 Author型別的物件。
“as” 運算子
“as”運算子跟”is”運算子有類似的行為,唯一的差異是如果兩個物件是型別相容的會傳回物件,不是的話傳回 null。
了解下面的程式碼看看。
public static string GetAuthorName(dynamic obj) { Author authorObj = obj as Author; return (authorObj != null) ? authorObj.Name : string.Empty; }
我們有一個方法接受動態物件如果物件是 Author型別就傳回物件名稱屬性。
這裡,我們宣告兩個物件:
var speaker = new Speaker { Name="Gaurav Kumar Arora"}; var author = new Author { Name = "Gaurav Kumar Arora" };
下面的程式碼傳回 “Name” 屬性:
var authorName = GetAuthorName(author); Console.WriteLine("Author name is:{0}", authorName);
它傳回空字串:
authorName = GetAuthorName(speaker); Console.WriteLine("Author name is:{0}", authorName);
34. 如何使用 Nullable<>型別?
可為 null的型別是包含已定義的資料型別或是空值的資料型別。
可為 null的型別跟 ”var”不相容。
任何資料型別可以透過”?”運算子的協助定義為可為 null的型別。
例如下面的程式碼宣告 int ‘i’ 是空的。
int? i = null;
前面有討論過 “var”不相容於可為 null的型別,所以如果你宣告下面這樣的程式,會有錯誤產生。
var? i = null;
35. 方法可以被重載的方式有哪些?
方法重載是一種實現編譯時期多型的方式,其中我們可以使用名稱相同但簽章不同的方法,例如下面的程式碼範例具有一個方法卷,這個方法卷是基於參數的數量和型別及傳回值而具有三個不同的簽章。
範例
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Hello_Word { class overloding { public static void Main() { Console.WriteLine(volume(10)); Console.WriteLine(volume(2.5F, 8)); Console.WriteLine(volume(100L, 75, 15)); Console.ReadLine(); } static int volume(int x) { return (x * x * x); } static double volume(float r, int h) { return (3.14 * r * r * h); } static long volume(long l, int b, int h) { return (l * b * h); } } }
注意
如果我們有一個兩個物件型別參數的方法跟有兩個整數(integer)型別參數的相同方法,當我們呼叫有int整數值的方法時,它會有integer整數參數的方法而不是物件行別的方法。
36. 什麼是.Net的物件池?
.Net的物件池允許物件保留在記憶池中這樣物件可以重複被使用不須再次建置,這裡要解釋物件池是什麼以及如何實作。
這是什麼意思?
物件池是準備使用的物件容器,每當有新物件的請求時,物件池管理員會接受該請求並從池中配置一個物件來滿足該請求。
它是如何運作的?
我們會為此目的使用工廠模式(Factory pattern),我們會有一個 factory方法會處理物件的建置,每當有新物件的請求時,factory方法會查看物件池(我們使用 Queue物件),如果在允許的限制範圍內有任何可用的物件,就回傳該物件(value物件),否則,會有新的物件被建置並退還給你。
37. 什麼是泛型?
泛型允許你延遲類別或方法中程式元素資料型別的指定,直到程式中真正使用它為止,換句話說泛型允許你撰寫可與任何資料型別一起使用的類別或方法。
你可以撰寫類別或方法的規格,有替代參數給資料型別,當編譯器碰到類別的建構子或是方法的函式呼叫時,它會產生程式碼來處理特定的資料型別。
泛型的類別跟方法結合了可重用性、型別安全及效率,這是非泛型的類別跟方法所沒有的,泛型最常用於集合及其上的方法操作,.NET Framework 類別函式庫 2.0版提供了一個新的命名空間,System.Collections.Generic,提供了幾個新的泛型基礎的集合類別,建議所有目標.NET Framework 2.0及以後的應用程式使用新的泛型集合類別以取代較早非泛型的類別跟方法像是 ArrayList。
泛型的特徵
泛型是一種透過下面的方式來豐富你的程式:
- 它可以幫助你讓程式碼可以重複使用、型別安全及效能。
- 你可以建置泛型集合類別, .NET架構類別函式庫包含幾個新的泛型集合類別在 System.Collections.Generic命名空間,你可以在System.Collections裡使用這些泛型集合類別而非集合類別。
- 你可以建置你自己的泛型介面、類別、方法、事件及委派。
- 你可以建置受約束的泛型類別以啟用特定資料型別的方法存取。
- 你可會在執行時期使用反射來取得使用在泛型資料型別中的型別資訊。
38. 描述存取修飾詞
存取修飾詞是用來指定成員或型別宣告存取的關鍵字。
存取修飾詞是用於指定型別成員或型別本身可存取範圍的關鍵字,例如,public class 可以存取整個世界,而 internal class只能存取到組件 (assembly)。
為什麼要使用存取修飾詞?
存取修飾詞是物件導向程式語言的組成部分,存取修飾詞用來實作OOP的封裝,存取修飾詞允許你定義誰有權來存取某些功能。
在 C#裡有6種不同的存取修飾詞:
修飾詞 |
描述 |
public |
存取公共成員沒有任何限制 |
private |
存取僅限於類別定義內,如果未正式指定這是預設的存取修飾種類 |
protected |
存取限於類別定義中以及繼承自該類別的任何類別中 |
internal |
存取只限於當前專案組件內的類別定義 |
protected internal |
存取限於當前的組件及衍生自其包含類別中的型別,在當前專案裡的所有成員及衍生類別中的所有成員都可以存取該變數 |
private protected |
存取限於當前組件中所包含的類別或是衍生自其類別的型別 |
39. 什麼是虛擬方法?
虛擬方法是可以在衍生類別中重新定義的方法,虛擬方法在基礎類別及衍生的類別中有一個實作,它在方法的基本功能相同時使用,但有時候更多的功能在衍生類別中需要時使用,虛擬方法在基礎類別中建置,這可以在衍生類別中覆寫,我們可以使用virtual關鍵字來建置虛擬方法而這個方法可以使用override關鍵字在衍生類別中覆寫。
當方法在基礎類別中被宣告為虛擬方法時,那方法就可以定義在基礎類別中,並且在衍生類別中可選擇是否要覆寫該方法。複寫方法也為方法提供了多種形式,因此,這也是多型的範例。
當方法在基礎類別中被宣告為虛擬方法和在衍生類別中方法有相同的定義,這時在衍生類別中不需再複寫它,但是當虛擬方法在基礎類別跟衍生類別中中有不同的定義時,那麼在衍生類別中就需要覆寫這個方法。
當虛擬方法被調用的時候,會檢查物件執行時期的型別是否有覆寫成員,最末層衍生類別中的覆寫成員會被呼叫,如果沒有衍生類別來覆寫成員就會是原始成員被呼叫。
虛擬方法
- 預設情況下方法是非虛擬的,我們不能覆寫非虛擬的方法。
- 我們可以將 virtual修飾詞與 static、 abstract、 private或 override 等修飾詞一起使用。
40. Array跟 ArrayList的差異?
這裡有一個差異列表:
Array |
ArrayList |
Array使用向量陣列來儲存元素 |
ArrayList使用 LinkedList來儲存元素 |
陣列的大小必須定義直到使用redim(vb) |
不需要指定儲存的大小 |
Array是一種特定資料型別的儲存 |
ArrayList以物件來儲存任何東西 |
不需要做型別轉換 |
每一次都要做型別轉換 |
不會產生執行時期的異常 |
會產生執行時期的錯誤異常 |
元素之間不能新增或刪除 |
元素可以新增刪除 |
沒有內建的成員可以做遞增或遞減排序 |
ArrayList有很多方法可以操作像是sort、insert、remove、binarysearch等等 |
41. 數值型別跟參考型別是什麼?
在 C#裡,資料型別可以是數值型別也可以是參考型別,數值型別變數直接包含他們的物件(或資料),如果我們複製一個數值型別變數到另一個,那麼我們就會產生物件的拷貝給第二個變數,他們在其值上可以獨自操作,數值型別資料型別儲存在堆疊上,而參考資料型別除存在堆積上。
在 C#中基礎資料型別包括 int、 char、 bool及 long是數值型別,類別跟集合是參考型別。
42. 序列化是什麼?
C#的序列化是將物件轉換為位元組串流來儲存物件至記憶體、資料庫或是檔案中,其目的是儲存物件的狀態以便在需要的時候重新建置,相反的程序稱作反序列化。
序列化有三種類型:
- 二進位序列化(儲存你的物件資料為二進位格式)。
- 簡單物件存取協定的序列化(Soap Serialization) (儲存你的物件資料為二進位格式;主要用在網路相關的通訊上)。
- XML 序列化(XmlSerialization) (儲存你的物件資料為XML檔)。
43. 如何使用“using”敘述?
使用using關鍵字有兩種方式,一個是做指示詞,另一個就是做敘述使用,我們來說明一下!
1.using 指示詞
通常我們使用 using關鍵字在後製程式碼及類別中新增命名空間,這樣就可以在目前的頁面上使用類別、介面及抽象類別跟他們的方法及屬性,新增命名空間可以用下面的兩種方式做:
A. 允許在命名空間中使用一般的類別
using System.IO; using System.Text;
B. 建置命名空間或型別的別名,這會呼叫一個 using別名的指示詞
using MyProject = TruckingApp.Services;
2.Using 敘述
這是另外一種使用 using關鍵字的方式,在改善記憶體回收的效能上扮演重要的角色。
44. 不規則陣列是什麼?
不規則陣列是它的元素也是陣列,不規則陣列的元素可以有不同維度及大小,不規則陣列有時也稱作”陣列的陣列”。
陣列的特殊型別已經介紹過,不規則陣列是陣列的陣列其中每個陣列的長度及索引可能不同。
範例
int[][] jagArray = new int[5][];
在上面的宣告中,列的大小是固定的,但是欄位沒有指定,因為它們可以有變化。
宣告跟初始化不規則陣列。
int[][] jaggedArray = new int[5][]; jaggedArray[0] = new int[3]; jaggedArray[1] = new int[5]; jaggedArray[2] = new int[2]; jaggedArray[3] = new int[8]; jaggedArray[4] = new int[10]; jaggedArray[0] = new int[] { 3, 5, 7, }; jaggedArray[1] = new int[] { 1, 0, 2, 4, 6 }; jaggedArray[2] = new int[] { 1, 6 }; jaggedArray[3] = new int[] { 1, 0, 2, 4, 6, 45, 67, 78 }; jaggedArray[4] = new int[] { 1, 0, 2, 4, 6, 34, 54, 67, 87, 78 };
45. .NET多執行緒是什麼?
多執行緒允許程式同時執行多個執行緒,這裡會說明在.NET中多執行緒如何運作,包含執行緒所有的範圍從執行緒的建置、競爭情況、死結、監控、互斥鎖、同步跟號誌(semaphores)等等。
執行緒的真正使用不是關於單一的循序執行緒,而是關於在單一的程式中使用多個執行緒,多個執行緒在相同的時間執行並執行不同的工作這樣子才叫做多執行緒,執行緒被認為是一個輕量級的程序,因為它在程式的上下文之間執行,並利用該程式為其分配的資源。
單執行緒排程只有一個執行緒而多執行緒排程含有一個以上的執行緒來執行。
46. 匿名型別是什麼?
匿名型別允許我們建置新的型別而不用定義,這是一個在單一的物件裡定義唯讀屬性卻不必明確地定義每個型別的方式,在這裡,型別是由編譯器產生,並且只在目前的程式碼區塊中存取,屬性的型別由編譯器來推斷。
我們可以透過使用“new”關鍵字和物件的初始化程序來建置匿名型別。
範例
var anonymousData = new { ForeName = "Jignesh", SurName = "Trivedi" }; Console.WriteLine("First Name : " + anonymousData.ForeName);
匿名型別使用 LINQ範例
匿名型別也可以跟 LINQ查詢運算式的 “Select”子句一起使用來傳回屬性的子集合。
範例
假如有個物件集合有屬性叫做 FirstName、 LastName、 DOB…等等,你只想在查詢資料後取得 FirstName跟 LastName,那麼:
class MyData { public string FirstName { get; set; } public string LastName { get; set; } public DateTime DOB { get; set; } public string MiddleName { get; set; } } static void Main(string[] args) { // Create Dummy Data to fill Collection. List < MyData > data = new List < MyData > (); data.Add(new MyData { FirstName = "Jignesh", LastName = "Trivedi", MiddleName = "G", DOB = new DateTime(1990, 12, 30) }); data.Add(new MyData { FirstName = "Tejas", LastName = "Trivedi", MiddleName = "G", DOB = new DateTime(1995, 11, 6) }); data.Add(new MyData { FirstName = "Rakesh", LastName = "Trivedi", MiddleName = "G", DOB = new DateTime(1993, 10, 8) }); data.Add(new MyData { FirstName = "Amit", LastName = "Vyas", MiddleName = "P", DOB = newDateTime(1983, 6, 15) }); data.Add(new MyData { FirstName = "Yash", LastName = "Pandiya", MiddleName = "K", DOB = newDateTime(1988, 7, 20) }); } var anonymousData = from pl in data select new { pl.FirstName, pl.LastName }; foreach(var m in anonymousData) { Console.WriteLine("Name : " + m.FirstName + " " + m.LastName); } }
47. 什麼是雜湊表?
雜湊表是一個儲存(鍵、值)對的集合,在這裡 Key用來尋找存儲位置,是不可變的且不能有重複的項目在雜湊表中, .Net架構已提供雜湊表類別,這類別包含所有執行雜湊表的功能不需要另外的開發,雜湊表有一般功能的字典集合,集合中的每個項目有兩個屬性的 DictionaryEntry物件:一個是鍵物件,一個是值物件,這些稱為鍵/值,當有項目新增到雜湊表中時,雜湊的程式碼就自動產生,這個程式碼對程式設計員隱藏,存取表的值要使用識別的鍵物件來達成,因為集合中的項目是根據隱藏的雜湊程式碼儲存,所以項目可以看作是隨機排序。
雜湊表集合
基本的類別函式庫提供了定義在 System.Collections命名空間中的雜湊表類別,所以你不必撰寫自己的雜湊表程式,它會在你每次新增的時候處理每個鍵,然後使用雜湊程式碼來讓搜尋更快,雜湊表的容量就是雜湊表所能容納的元素數目,當元素被新增至雜湊表中時,容量會透過重新配置需要的記憶體來自動增加,它是較舊的 .Net架構型別。
宣告雜湊表
雜湊表類別可以在命名空間 System.Collections中找到,所以執行範例前,必須新增using System.Collections; 到原始碼程式中,宣告雜湊表如下:
Hashtable HT = new Hashtable ();
48. LINQ是什麼?
LINQ代表語言整合查詢, LINQ 是一種資料查詢的方法,可使用類似於SQL查詢的語法來為 .NET語言提供查詢的功能。
LINQ具有查詢任何資料來源的強大功能,資料來源可以是物件的集合、資料庫或是XML檔案,我們從任何實作 IEnumerable<T>介面的物件來擷取資料。
LINQ的優點
- LINQ提供了一種物件基礎語言整合的方式來查詢資料,這些資料不管其來源何處都可以查詢,所以透過 LINQ,我們可以查詢資料庫、XML跟集合。
- 編譯時期語法的檢查。
它允許你在應用程式的原生語言像是VB或C#中用跟使用SQL查詢資料庫一樣的方式來查詢像是陣列、列舉類別等等的集合。
49. C# .Net的檔案處理是什麼?
System.IO 命名空間提供四個類別允許你來操作單個檔案及與計算機目錄結構相互作用,目錄及檔案直接擴充 System.Object,並使用各種靜態方法來支援檔案的新增、複製、移動及刪除,它們只有靜態方法而且不能被實體化,FileInfo跟 DirecotryInfo 型別衍生自抽象類別 FileSystemInfo型別,它們通常用於獲得檔案或目錄的詳細資料,因為他們的成員傾向傳回強型別物件,它們實作與目錄及檔案大致相同的公用方法,他們是有狀態的而且其類別的成員並非靜態的。
50. 反射是什麼?
反射(Reflection)是執行階段型別探索的過程用來檢測中繼資料(Metadata)、通用中間語言(CIL code)、晚期繫結跟自生成程式碼,在執行時期透過使用反射,我們可以存取跟在設計時期使用反組譯工具所顯示的相同”型別”資訊,反射類似於逆向工程,在逆向工程中我們可以破壞現有編譯好的 *.exe 或 *.dll以探索定義好的重要內容資訊,包括方法、欄位、事件及屬性。
你可以使用 System.Reflection命名空間來動態探索給訂的型別所支援的介面集。
反射通常用於頃印輸出載入的組譯列表、檢查方法跟屬性的參考等等,反射也用在外部的反組譯工具像是Reflector、 Fxcop跟 NUnit,因為 .NET工具不需要解析類似於C++的原始碼。
中繼資料的研究
下面的程式透過新增一主控台應用程式來描述反射的過程,這支程式會顯示 mscorlib.dll組譯程式裡所有型別的欄位、方法、屬性及介面,在進行前必須導入”System.Reflection”。
在這裡我們在程式類別中定義了許多靜態方法來列舉欄位、方法及介面,靜態方法使用一個 “System.Type”參數並且回傳 void。
static void FieldInvestigation(Type t) { Console.WriteLine("*********Fields*********"); FieldInfo[] fld = t.GetFields(); foreach(FieldInfo f in fld) { Console.WriteLine("-->{0}", f.Name); } } static void MethodInvestigation(Type t) { Console.WriteLine("*********Methods*********"); MethodInfo[] mth = t.GetMethods(); foreach(MethodInfo m in mth) { Console.WriteLine("-->{0}", m.Name); } }
※2022/03/22後記
c#教學:
雖然不知是否為真,但謝謝您的文章
題目出得不錯,但答案說明不夠清楚
技術文章要寫得讓人看的懂不簡單