解析“60k”大佬的19道C#面試題(下)
阿新 • • 發佈:2020-03-30
# 解析“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#` 簽名也能不用寫引數了吧,因為引數確實太長了