C 中居然也有切片語法糖,太厲害了

2021-10-21 15:12:22 字數 3964 閱讀 7839

一:背景

講故事昨天在 github 上準備找找 c# 9 又有哪些新語法糖可以試用,不覺在乙個文件上看到乙個很奇怪的寫法: foreach (var item in myarray[0…5]) 哈哈,熟悉又陌生,玩過python的朋友對這個 [0…5] 太熟悉不過了,居然在 c# 中也遇到了,開心哈,看了下是 c# 8 的新語法,諷刺諷刺,8 都沒玩熟就搞 9 了,我的探索欲比較強,總想看看這玩意底層是由什麼支撐的。

二:… 語法糖的用法

從前面介紹的 myarray[0…5] 語義上也能看出,這是乙個切分array的操作,那到底有幾種切分方式呢? 下面乙個乙個來介紹,為了方便演示,我先定義乙個陣列,**如下:

var myarr = new string ;

提取 arr 前3個元素

如果用 linq 的話,可以用 take(3),用切片操作的話就是 [0…3], **如下:

static void main(string[

] args);//

1. 獲取陣列 前3個元素

提取 arr 最後三個元素

這個怎麼提取呢?在 python 中直接用 -3 表示就可以了,在c# 中需要用 ^ 來表示從末尾開始,**如下:

static void main(string[

] args);//

1. 獲取陣列 最後3個元素

var query1 = myarr[^3

..];

var query2 = myarr.skip(myarr.length -3)

.tolist();

console.writeline($"query1=");

console.writeline($"query2=");

}

提取 array 中index = 4,5,6 的三個位置元素

用 linq 的話,就需要使用 skip + take 雙組合,如果用切片操作的話就太簡單了。。。

static void main(string[

] args);//

1. 獲取陣列 中 index=4,

5,6 三個位置的元素

var query1 = myarr[4.

.7]; var query2 = myarr.skip(4)

.take(3)

.tolist();

console.writeline($"query1=");

console.writeline($"query2=");

}

從上面的切割區間 [4…7] 的輸出結果來看,這是乙個 左閉右開 的區間,所以要特別注意一下。

獲取 array 中倒數第三和第二個元素

從要求上來看就是獲取元素 80 和 90,如果你理解了前面的兩個用法,我相信這個你會很快的寫出來,**如下:

static void main(string[

] args);//

1. 獲取 array 中倒數第三和第二個元素

var query1 = myarr[^3

..^1

];var query2 = myarr.skip(myarr.length -3)

.take(2)

.tolist();

console.writeline($"query1=");

console.writeline($"query2=");

}

三. **原理

通過前面 4 個例子,我想大家都c#教程知道怎麼玩了,接下來就是看看到底內部是用什麼做支撐的,這裡使用 dnspy 去挖挖看。

從 myarr[0…3] 看起

用 dnspy 反編譯**如下:

//編譯前

var query1 = myarr[0.

.3];//編譯後:

string[

] query = runtimehelpers.getsubarray

(myarr, new range(0,

3));

從編譯後的**可以看出,原來獲取切片的 array 是呼叫 runtimehelpers.getsubarray 得到了,然後我簡化一下這個方法,**如下:

public static t[

] getsubarray<

[nullable(2)

] t>

(t array, range range

)

從上面**可以看到,最後的 子array 是由 buffer.memmove 完成的,但是給 子array 的切割位置是由 getoffsetandlength 方法實現,繼續追一下**:

public readonly struct range : iequatable

public index end

public range(index start, index end)

public valuetuple<

int,

int> getoffsetandlength(

int length)

else

index end = this.end;

int num2;

if(end.isfromend)

else

return new valuetuple<

int,

int>

(num, num2 - num);}

}

看完上面的**,你可能有兩點疑惑:

start.isfromend 和 end.isfromend 是什麼意思。

其實看完上面**邏輯,你就明白了,isfromend 表示起始點是從左開始還是從右邊開始,就這麼簡單。

我並沒有看到 start.isfromend 和 end.isfromend 是怎麼賦上值的。

在 index 類的建構函式中,取決於上一層怎麼去 new index 的時候塞入的 true 或者 false,如下**:

這個例子的流程大概是: new range(1,3) -> operator index(int value) -> fromstart(value) -> new index(value) ,可以看到最後在 new 的時候並沒有對可選引數賦值。

** myarr[^3…]

剛才的例子是沒有對可選引數賦值,那看看本例是不是 new index 的時候賦值了?

//編譯前:

var query1 = myarr[^3

..];

//編譯後:

string[

] query = runtimehelpers.getsubarray

(myarr, range.startat(new index(

3, true)))

;

看到沒有,這一次 new index 的時候,給了 isfromend = true , 表示從末尾開始計算,大家再結合剛才的 getoffsetandlength 方法,我想這邏輯你應該理順了吧。

四:總結

總的來說這個切片操作太實用了,作用於 arr 可以大幅度減少對 skip & take 的使用,作用於 string 也可以大幅減少 substring 的使用,如:「12345」[1…3] -> 「12345」.substring(1, 2),嘿嘿,厲害了吧! 還是c# **??

C 中居然也有切片語法糖,太厲害了

昨天在 github 上準備找找 c 9 又有哪些新語法糖可以試用,不覺在乙個文件上看到乙個很奇怪的寫法 foreach var item in myarray 0.5 哈哈,熟悉又陌生,玩過python的朋友對這個 0.5 太熟悉不過了,居然在 c 中也遇到了,開心哈,看了下是 c 8 的新語法,...

C 中建構函式居然是可以直接呼叫的

如下 include class a a int x a x b x 1 a int geta int getb private int a int b int main 經過編譯和執行之後,結果如下 呼叫第乙個建構函式 呼叫第二個建構函式 呼叫析構函式 5 6 呼叫析構函式 首先要知道 一 c 編...

C 中建構函式居然是可以直接呼叫的

如下 include class a a int x a x b x 1 a int geta int getb private int a int b int main 經過編譯和執行之後,結果如下 呼叫第乙個建構函式 呼叫第二個建構函式 呼叫析構函式 5 6 呼叫析構函式 首先要知道 一 c 編...