2020年最新 C# .net 面試題,月薪20K+中高階/架構師必看(一)
筆者近幾年前前後後面試了50+公司左右,怎麼講呢,每個面試官的風格都不一樣,要問的問題也不盡相同。但是面試是需要技巧的,提前準備工作以及如何把簡歷寫得讓人眼前一亮是很有必要的,關於這一塊將在其它篇幅作介紹。話不多說,先總結出面試遇到的主流面試題,將分8篇文章,每篇25道,幾乎涵蓋90%以上的面試知識點,適用於中高階、架構師去複習,暫時還那麼多時間把答案寫出來,後續會加上,如果大家有好的答案也可以在評論區寫出來,謝謝大家。
一、如何在.NET中做deep copy?
二、throw與throw e的區別?應該用哪一個?
using System; using System.Text; namespaceConsoleApp4 { class Program { static void Main(string[] args) { try { Method2(); } catch (Exception ex) { Console.Write(ex.StackTrace.ToString()); Console.ReadKey(); } }private static void Method2() { try { Method1(); } catch (Exception ex) { throw; } } private static void Method1() { try {throw new Exception("Inside Method1"); } catch (Exception) { throw; } } } }
上面的程式碼輸出的結果如下:顯示了完整的層次結構和引發異常的方法名稱。
現在單獨把Method2()方法中的異常丟擲throw改成throw ex。
private static void Method2() { try { Method1(); } catch (Exception ex) { throw ex; } }
接著再把程式執行一遍,執行的結果如下所示:可以看出引發異常的Method1並沒有被丟擲。throw ex忽略所有先前的層次結構,重置了堆疊跟蹤,丟擲的異常將成為“原始”異常,因此所有先前的堆疊跟蹤都不會存在。
結論:引發異常後,堆疊攜帶的部分資訊就是堆疊跟蹤。堆疊跟蹤是方法呼叫層次結構的列表,該列表以引發異常的方法開始並以捕獲異常的方法結束。如果通過在throw語句中指定異常來重新引發異常(也就是使用throw ex),則堆疊跟蹤將從當前方法重新啟動,並且引發該異常的原始方法與當前方法之間的方法呼叫列表將丟失。要使原始堆疊跟蹤資訊與該異常保持一致,請使用throw語句而不指定該異常。
三、finally block是什麼時候呼叫的?
先看程式碼如下:
static void Main(string[] args) { Console.WriteLine(GetNum()); } public static int GetNum() { int Num=1; try { Console.WriteLine("try"); return Num; } catch (Exception ex) { throw ex; } finally { ++Num; Console.WriteLine("finally"); } }
結論:try中的return語句先於finally中的函式執行所以,返回的結果是1, 而不是2。
從執行結果可以看出,return語句執行後,將把返回結果放置進函式棧中,此時函式並不是馬上返回,它要執行finally語句後才真正開始返回。
四、out和ref有什麼區別?
五、在關係型資料庫裡,referential integrity(應用完整性)是什麼意思?
六、解釋this關鍵字?它可以在靜態方法中使用嗎?
七、在GOF設計模式的3類中各選一個設計模式來說明其用途?
八、mock和stub有啥區別。
九、SoC是什麼意思?
十、cross cutting concen(縱切關注)最好怎麼處理?
十一、在EF中如何定義多對多關係?
public class Media // One entity table { public int Id { get; set; } public string Name { get; set; } public bool Enabled { get; set; } public virtual ICollection<ContractMedia> ContractMedias { get; set; } } public class Contract // Second entity table { public int Id { get; set; } public string Code { get; set } public virtual ICollection<ContractMedia> ContractMedias { get; set; } } public class ContractMedia // Association table implemented as entity { public int MediaId { get; set; } public int ContractId { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public double Price { get; set; } public virtual Media Media { get; set; } public virtual Contract Contract { get; set; } }
建立模型/實體後,需要在上下文中定義關係:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<ContractMedia>() .HasKey(c => new { c.MediaId, c.ContractId }); modelBuilder.Entity<Contract>() .HasMany(c => c.ContractMedias) .WithRequired() .HasForeignKey(c => c.ContractId); modelBuilder.Entity<Media>() .HasMany(c => c.ContractMedias) .WithRequired() .HasForeignKey(c => c.MediaId); }
十二、DateTime.Parse(myString) 這段程式碼有什麼問題?應該怎麼寫?
應該使用TryParse,因為如果無法解析,Parse將會引發異常。
正確寫法:
DateTime dt; DateTime.TryParse(myString,out dt);
十三、為什麼catch(Exception)是個不好的寫法?
catch(Exception):指定出現異常在catch塊要處理,僅知道已引發異常,但是無法獲取有關此異常的更多資訊。
應該使用catch(Exception ex),catch(Exception ex) 傳遞引發的實際異常的例項,不但可以捕獲異常並能獲取異常的物件,可以檢索出更多的異常相關的資訊。
十四、.NET的垃圾回收是怎麼管理CLR中的物件的生命週期的?
垃圾回收是.net中的執行時CLR這個庫中的一個核心功能,目的就是為了提高記憶體的利用率。值型別這些變數,用完以後系統就立刻把這個記憶體銷燬了。堆裡面的記憶體如何被回收釋放?不確定,一般都是當程式需要新記憶體,或者記憶體不夠的時候開始執行回收。當然什麼物件被回收什麼物件不會被回收,垃圾回收機制它有選擇,當這個物件沒有用的時候,也就是沒任何地方引用這個物件的時候就會被回收。
使用代的機制來回收,建立物件的時候,會分為3個代:第0代、第1代、第2代。建立物件的時候,每個代都有個初始大小,比如1M。每次建立新物件的時候都是向第0代開始建立,當第0代記憶體滿後,就會執行垃圾回收機制,把沒有任何引用的物件給回收銷燬,然後把有引用的物件,也就是沒被回收活下來的物件放到第1代。然後再次建立物件還是向第0代裡面放,如此類推。如果說到了要回收第2代的時候,活下來的物件還是放到第二代,程式繼續執行,直到這3代空間都滿了,就會嘗試把每一代的記憶體容量擴大。也不是一直擴充,後續擴充後記憶體還是滿了就會拋異常了。
C/C++中由程式設計師進行物件的回收像學校食堂中由學生收盤子,.Net中由GC進行垃圾回收像餐館中店員去回收。
GC是垃圾收集器(Garbage Collection)。程式設計師不用擔心記憶體管理,因為垃圾收集器會自動進行管理。GC只能處理託管記憶體資源的釋放,對於非託管資源則不能使用GC進行回收,必須由程式設計師手工回收,一個例子就是FileStream或者SqlConnection需要程式設計師呼叫Dispose進行資源的回收。
要請求垃圾收集,可以呼叫下面的方法:GC.Collect()一般不需要手動呼叫GC.Collect()。當一個物件沒有任何變數指向(不再能使用)的時候就可以被回收了。
基礎知識:當沒有任何變數指向一個物件的時候物件就可以被回收掉了,但不一定會立即被回收。
object obj = new object();//只有new才會有新物件
Console.WriteLine(obj);
object obj2 = obj;
obj = null;
obj2 = null;
Console.WriteLine();
十五、Finalize()和Dispose()這2個方法有什麼不同。
Finalize():解構函式的函式名跟建構函式一致,前面加了~符號,.net裡面其實把解構函式叫終結函式,解構函式會被編譯成Finalize函式。
作用:一般我們不用它,我們普通的託管資源垃圾會自動回收,但是假設我們類中訪問了作業系統的資源,如非託管資源,垃圾是不會回收的。而在執行垃圾回收之前會先呼叫我們的解構函式進行非託管資源的釋放然後再進行垃圾回收。
但是非託管資源,一般當物件不用的時候就應該要立馬回收,而不是等待回收。所以多了dispose函式,一般類中有呼叫非託管資源,類就會繼承IDisposable介面,然後實現Dispose方法,在Dispose中去釋放非託管資源。在呼叫非託管資源的時候,再去調Dispose方法就會立馬進行垃圾回收,所以一般我們不會去使用解構函式這個終結器。如果使用using的話,那就連Dispose方法都不需要手動呼叫了,會自動呼叫這個方法,執行垃圾回收。
十六、a.Equals(b) 和 a==b有什麼不同?寫出下列程式碼中Console打印出的結果。
static void Main(string[] args) { int a = 10; int b = 10; Console.WriteLine(a==b); Console.WriteLine(a.Equals(b)); StringBuilder sb1 = new StringBuilder("Asp.Net"); StringBuilder sb2 = new StringBuilder("Asp.Net"); Console.WriteLine(sb1 == sb2); Console.WriteLine(sb1.Equals(sb2)); }
解答:1、對於值型別:= = 和equals等價,都是比較儲存資訊的內容是否相等。
2、對於引用型別:= = 比較的是引用型別在棧中的地址,equals方法則比較的是引用型別在堆中的儲存的內容是否相等。
十七、object identity(同一性)比較和object equality(相等性)比較有什麼不同?
解答:相等性:兩個物件,它們的值相等。
同一性:兩個物件,它們的引用相等。
十八、請列出常見的快取方式,並簡要概述其優缺點。
十九、請使用linq語句表示式,查詢出 int values={1,2,5,2,3,5,5,3,4,6,3,3};中出現次數最多的數字。
二十、WebService、WCF、WebApi有什麼不同?
二十一、可以採用foreach迭代的類的物件必須滿足什麼條件?
二十二、列舉C#依賴注入的方式,且相關優劣勢說明。
二十三、async標記的方法返回值有何要求?
二十四、sleep()和wait()有什麼區別?
二十五、C#中Params是什麼含義?有何用途?