1. 程式人生 > >解析“60k”大佬的19道C#面試題(下)

解析“60k”大佬的19道C#面試題(下)

# 解析“60k”大佬的19道C#面試題(下) 在上篇中,我解析了前 `10` 道題目,本篇我將嘗試解析後面剩下的所有題目。 > 姐妹篇:[解析“60k”大佬的19道C#面試題(上)](https://www.cnblogs.com/sdflysha/p/20200325-19-csharp-interview-question-from-60k-boss-1.html) 這些題目確實不怎麼經常使用,因此在後文中,我會提一組我的私房經典“`6k`面試題”,供大家輕鬆一刻。 ## 先略看題目: 11. 簡述 `LINQ` 的 `lazy computation` 機制 12. 利用 `SelectMany` 實現兩個陣列中元素做笛卡爾集,然後一一相加 13. 請為三元函式實現柯里化 14. 請簡述 `ref struct` 的作用 15. 請簡述 `ref return` 的使用方法 16. 請利用 `foreach` 和 `ref` 為一個數組中的每個元素加 `1` 17. 請簡述 `ref` 、 `out` 和 `in` 在用作函式引數修飾符時的區別 18. 請簡述非 `sealed` 類的 `IDisposable` 實現方法 19. `delegate` 和 `event` 本質是什麼?請簡述他們的實現機制 # 解析: ## 11. 簡述 `LINQ` 的 `lazy computation` 機制 `Lazy computation` 是指延遲計算,它可能體現在**解析階段**的**表示式樹**和**求值階段**的**狀態機**兩方面。 首先是**解析階段**的**表示式樹**, `C#` 編譯器在編譯時,它會將這些語句以表示式樹的形式儲存起來,在求值時, `C#` 編譯器會將所有的 `表示式樹` 翻譯成求值方法(如在資料庫中執行 `SQL` 語句)。 其次是**求值階段**的**狀態機**, `LINQ to Objects` 可以使用像 `IEnumemrable` 介面,它本身不一定儲存資料,**只有在求值時**,它返回一個迭代器—— `IEnumerator` ,它才會根據 `MoveNext()` / `Value` 來求值。 這兩種機制可以確保 `LINQ` 是可以延遲計算的。 ## 12. 利用 `SelectMany` 實現兩個陣列中元素做笛卡爾集,然後一一相加 ``` csharp // 11. 利用 `SelectMany` 實現兩個陣列中元素的兩兩相加 int[] a1 = { 1, 2, 3, 4, 5 }; int[] a2 = { 5, 4, 3, 2, 1 }; a1 .SelectMany(v => a2, (v1, v2) => $"{v1}+{v2}={v1 + v2}") .Dump(); ``` 解析與說明:大多數人可能只瞭解 `SelectMany` 做一轉多的場景(兩引數過載,類似於 `flatMap` ),但它還提供了這個三引數的過載,可以允許你做多對多——笛卡爾集。因此這些程式碼實際上可以用如下 `LINQ` 表示: ``` csharp from v1 in a1 from v2 in a2 select $"{v1}+{v2}={v1 + v2}" ``` 執行效果完全一樣。 ## 13. 請為三元函式實現柯里化 解析:柯里化是指將 `f(x, y)` 轉換為 `f(x)(y)` 的過程,三元和二元同理: ``` csharp Func op3 = (a, b, c) => (a - b) * c; Func>> op11 = a => b => c => (a - b) * c; op3(4, 2, 3).Dump(); // 6 op11(4)(2)(3).Dump(); // 6 ``` 通過實現一個泛型方法,實現**通用的**三元函式柯里化: ``` csharp Func>> Currylize3(Func op) { return a => b => c => op(a, b, c); } // 測試程式碼: var op12 = Currylize3(op3); op12(4)(2)(3).Dump(); // (4-2)x3=6 ``` > 現在瞭解為啥 `F#` 簽名也能不用寫引數了吧,因為引數確實太長了