D# 語法

這篇文章 隨意 的 記錄 關於 D# 語法的想法 。javascript

 

有關   ILBC / D# , 見 《ILBC 白皮書》   http://www.javashuo.com/article/p-bsuuysoc-bo.html    。html

 

 

D#  又名  Data ,  D++      。前端

 

D#  支持 結構體 方法 和 繼承 ,  D# 的 結構體 的 靜態方法 和 類 同樣, 實例方法 所有采用 擴展方法 的 方式 定義 。java

擴展方法 就是 C# 裏的 擴展方法 的 那種 擴展方法   。git

在一個 靜態方法 的 第一個參數 傳入 結構體 自身,    這就是 實例方法  。程序員

D# 結構體 實例方法 的 定義 比 C# 擴展方法 簡單,  不須要在 第一個 自身參數 前 加  「this」  關鍵字  。github

好比, A 結構體,數據庫

 

struct A編程

{json

          public static  void Foo( A a )

          {

                         // do something

          }

}

 

能夠這樣調用 :

 

A a;

……

a.Foo();

 

還能夠 經過 指針 的 方式 傳遞 自身參數,  好比 :

 

struct A

{

         int     i;

          public static  void Foo( A  *   a )

          {

                         int   j   =  a ->  i  ;

 

                         // do something

          }

}

 

能夠這樣調用 :

A a;

( & a ) ->  Foo()   ;

 

或者 :

A * a;

a   ->   Foo()   ;

 

嘿嘿嘿,     指針 的方式能夠用於 體積 比較大 的 結構體 。   總之 D# 提供了  靈活性   。

 

更靈活的是,  D# 容許 結構體 同時定義 按值傳遞 和 按指針傳遞   2 個 重載 的 實例方法,就是 :

 

 

struct A

{

          int     i;

 

          public static  void Foo( A a )

          {

                         // do something

          }

 

          public static  void Foo( A  *   a )

          {

                         int   j   =  a ->  i  ;

                         // do something

          }

}

 

能夠看到,    Foo( A a )   和   Foo( A * a )      能夠共存,    是  重載   。

 

固然, 指針操做 是 unsafe 操做,   全部的 指針 操做 都應該 用 unsafe 關鍵字 修飾,   因此 上述代碼 應該 加上 unsafe 關鍵字 ,以下:

 

struct A

{

          int     i;

 

          public static  void Foo( A a )

          {

                         // do something

          }

 

          unsafe   public static  void Foo( A  *   a )

          {

                         int   j   =  a ->  i  ;

                         // do something

          }

}

 

看 ,   在 Foo( A * a )    前面 加上了   unsafe  關鍵字,      對於  C# , unsafe 代碼 應包括在 unsafe 塊  中,

D# 簡化了 這個 作法,  不使用 unsafe 塊,   而是 用 unsafe 關鍵字 修飾 方法,

也能夠  修飾 類,     修飾 類 表示 整個類 都是 unsafe,  就不須要 每一個 方法 都用 unsafe 修飾 。

 

unsafe  沒有 實際意義,   只有 標記意義  。

unsafe 的 做用 就是 能夠經過 編譯器 查找 全部的 unsafe 方法 類,   讓 程序員 能夠容易的關注 unsafe 代碼   。

 

爲何 要 支持   結構體 和 unsafe  ?     由於 我以前在 《ILBC 規範》 中 說過,   D# 的 設計目標 之一 是 執行效率  。

 

具體的說,  D# 的 結構體 和 unsafe  主要會 用來 支持   底層應用,   基礎應用,   高性能中間件      。

除了 進程(線程)調度  、 IO 內核模塊 之外 ,    包括 套接字(Socket) 在內 的 底層模塊 均可以用 D# 編寫  。

事實上   只要 不使用 堆(new) 的話,   進程(線程)調度 、 IO 內核模塊 也能夠用 D# 編寫,

這種 狀況 基本上 所有是 unsafe  代碼 和  結構體,       這種狀況 差很少 和 C 等價  。

但 這樣的話,好像 還要 加一個 「開關」,  好比 編譯選項 什麼的,     在    「不使用 堆 的 模式 下」   編程(編譯)  。

這會讓 一個 語言 變得 複雜,      不太 符合 D# 的 初衷   。

實際上  進程(線程)調度  、 IO 內核模塊    這就是 C 的 事,不必 去和 C 搶  。   呵呵呵呵  。

 

C++  就是 那種   「只要 你 選擇,  什麼均可以作」   的 語言,  因而 呵呵 了   。

這就好像 有的 業務系統 寫的 很靈活,   什麼均可以作, 只要 你 去 配置,,,,,,

而後,靈活過頭了, 一堆配置,  等你學會 這堆 配置 , 也 差很少 成 這個 系統 的 專家 了 。   問題是 這個 系統 的 專家 只是在 你的 公司 裏 有用, 外面的 業界 對 此 沒什麼 承認    ,     而後 就 呵呵 了     。

 

那 什麼 是 基礎應用 呢 ?  好比    圖形圖像,2D 3D,遊戲,  視頻音頻, 視頻採樣,音頻採樣,人工智能,大數據,數據庫,文檔數據庫,大規模並行計算文檔數據庫,搜索引擎,爬蟲,編譯器  , 文本編輯器,  大型文本編輯器     ,,,

 

什麼事 高性能中間件  ?  好比 Nginx  ,    上面 基礎應用 中 提到的  文檔數據庫 之類 好像也能夠算   中間件   。  還有 搜索引擎 也算 中間件 ?    不知道   ……

 

結構體 由於 沒有 GC ,   因此  難以 應用於  對象圖(結構體 引用 圖),    可是 單純 在 棧 中 的 結構體 的 應用範圍 也頗有限 。

可是,  有一種 場合,  結構體 能夠 獲得 充分 的 使用,   就是 結構體 數組,

或者說,   一個 很大 的 數組 中 包含了 不少 的 結構體 。

這在 基礎應用 中 很普遍,

好比 上面說的    圖形圖像,2D 3D,遊戲,  視頻音頻, 視頻採樣,音頻採樣  ,,,,,,,

乃至於      人工智能,大數據,數據庫,文檔數據庫,大規模並行計算文檔數據庫,搜索引擎,爬蟲,編譯器  , 文本編輯器,  大型文本編輯器        都 與此有關  。

 

D#   對 結構體 和 指針 的 支持  很是 適合於 這些 場景   。

 

數組 自己 是 屬於 堆 的,  可是 數組 裏 包含了 大量 的 結構體   。

基礎應用 須要 對 這種 場景 很是良好 的 支持   。

因此 從 這裏 能夠看到,   D# 的 意義 之一,  開發 基礎應用 不必定 要用 C++  ,  如今 的 情況 就是 基本都用  C++   。

包括   圖形圖像  視頻音頻 人工智能   。

 

因此   D#   須要 對 數組 進行 比較 大力 的 支持,  包括 排序  等 , 其它 的 還有什麼?    想不出來,  你們想到能夠告訴我  。

C#  對 集合類 好比 List<T>  的 支持不少, 我也不太清楚,反正有不少 方法 和 擴展方法  。

 

不過 D#  首要支持 的是 數組, 別跟我說 什麼 List<T>  ,   C#  都 在 推   Span,  ArraySegment  了,  

D#  沒有那麼多 麻煩,    直接   數組 。

 

從 C# 的 Span, ArraySegment,  ArrayPool,  BufferManager     這一堆東西 能夠 看出來,  C#  目前 的 尷尬  。

這一堆東西 每個 都是 一套複雜的班子,   我又想起 前幾天 我查了一下 C# 的元組,  其實我須要的 只是  ( int i ,  string s )    這樣一個 小括號 而已,

結果 查出了 一大堆   名字空間 很長的東西, 裏面好像有  「Tuple」  字樣 , 又是 泛型, 又是 繼承,  好像 Visual Studio 還要裝什麼 安裝包,

而後  。

呀,  我怎麼 不知不覺  的 又 批評 C# 一下 了?

 

D# 對於 數組 的 操做 好比 排序 什麼的,  能夠在 指定 的 範圍(Range) 內 進行,   好比 數組 有  1000 個元素, 我只對 第 200 ~ 400 個 元素 排序,  這隻須要 在 調用 Sort()  方法 時  指定 開始 index 和 結束 index 就能夠了,好比:

 

int []    iArr   ;

……

iAr.Sort( 200 ,   400 )   ;

 

若是 實在 要 封裝,本身 弄個 結構體 或者 對象 把 開始 index 和 結束 index 包裝起來就行,好比:

 

struct    Span

{

            int   beginIndex,

            int   endIndex

}

 

void Foo( Span span )

{

          int []    iArr   ;

          ……

          iAr.Sort( span.beginIndex ,    span.endIndex )   ;

}

 

面向對象  愛好者 們 可能 喜歡 把 Sort()  也  封裝到 Span 裏,

 

class ArraySpan<T>

{

             private T  []       arr      ;

             private   int  beginIndex    ;

             private   int  endIndex     ;

 

             public   ArraySpan(T []   arr,   int  beginIndex,     int  endIndex)

             {

                        this.arr = arr;

                        this.beginIndex =  beginIndex ;

                        this.endIndex = endIndex ;

             }

 

             public Sort()

             {

                           this.arr.Sort( this.beginIndex,    this.endIndex )    ;

             }

}

 

這也能夠,   不過 這是 程序員 的 事,不是 語言 的 事,   或者說 這是 庫 的 事,  程序員 能夠把 本身的 封裝 貢獻出來,   做爲 庫  。

 

另外一方面 ,  List<T>  固然也要, 不只 List, 還有 Dictionary,  Queue,   LiinkedList,   Stack    這些都要,  D# 固然也要有 一個 System.Collections   。

不過 大量 數據 的 存儲操做 仍然是 數組   。

能夠用 List  來存放 多個 數組  。

 

通過一番思索(過程 略),  我決定這樣來設計  D#  的 數組 :

D#  有 2 種 數組,   unsafe 數組 和 託管數組,     unsafe 數組 也叫 原生數組,  就是一個 C 語言 數組, 也能夠說是 數組首地址,或者 指向 某個元素 的 指針,

託管數組 是一個 對象, 內部 包含了一個 unsafe 數組,  注意,  重點來了,

託管數組 是 用 泛型 實現的,   託管數組 是一個 泛型類,  

這樣,  對於 多維數組,  咱們 須要像 C# 的 Action 同樣,   預先定義好  多個  數組類型,   

 

就像 C# 的 Action 有 這些 定義 :

Action,  Action<T>,  Action<T1, T2>,    Action<T1, T2, T3>,     Action<T1, T2, T3, T4>  ,     ……

 

D# 的 會 定義這些 數組類型 :

Array1<T>,   Array2<T>,    Array3<T>,    Array4<T>,    Array5<T>,      ……

Array1 是 一維數組, Array2 是 二維數組, Array3 是 三維數組,   ……

 

D# 容許 給 索引器(中括號 [   ]   經過 逗號 隔開 傳入 多個 參數),

好比 

 

Array2<int>   iArr = new  Array2<int>()   ;

iArr  [  2,  3  ]    =   10   ;

 

這樣的話, 實際上,  程序員 徹底 能夠 本身 來 實現 託管數組,   能夠自由的 實現  n 維 的 託管數組,

也就是說,  託管數組  不是 語言 的 事,  是 庫 的 事,   程序員 能夠 自由 的 實現 託管數組,  能夠把本身 實現 貢獻 出來,  做爲 庫 。

社區 裏 能夠 有 多種 託管數組 的 實現(庫),   程序員 能夠 任意選擇 使用,  更能夠 本身實現 貢獻 。

這就是    「庫」   的    理念 。

這是一種 簡潔 開放 的 語言 理念  。

 

固然,   unsafe 數組 由  D# 提供  。

 

這樣的作法 有點可笑,  可是 很爽  。

 

咱們 再 回過頭來 看看 C#  的 數組, C# 有一個 Array 類, 內部 作了 不少工做, 可是這些工做 程序員 很難了解,  也不能參與, 這就是 C# 常常揮舞 的 」CLR 技術「 。

 

咱們再來看看 數組 的 方法, 好比 Sort()  方法  ,   咱們以 一維數組 爲例,   爲何要以 一維數組 爲例,  由於 多維 數組 的 排序 說不清楚,,固然,你能夠去 實現 你本身的 多維數組,包括排序 。

 

排序 中 會 對 元素 進行 比較 運算,   對於 基礎類型,  好比 int  float double,  比較運算  就是   > < == != >= <=   運算符  的  運算,

可是,爲了 複用代碼,  Array1<T>   但願 對於 全部 的  T 類型  都  可以 用 同一個 Sort()  方法,  這就須要 全部的 T 類型 都支持  > < == != >= <=   運算符  的  運算,

這就涉及到 運算符 重載  。

 

也能夠不用 運算符 重載, 而用 C#  裏的 Comparer 的 方式,   給 Sort( IComparer comparer )  方法 傳一個 Comparer  對象,

可是,  對於 基礎類型(int float double char  等) 來講,  調用 Comparer 的 方法 比較大小 比起 用   > < == != >= <=   運算符   會帶來一些 性能損耗 。

爲了 保持 基礎類型 的 原生效率,   因此 須要 運算符 重載  。

 

能夠聲明 這樣 一個  Template  :

 

template    TCompareOperator   <T>

{

              bool     operator    =     (  T  val1,    T   val2   )     ;

              bool     operator    >     (  T  val1,    T   val2   )     ;

              bool     operator    <     (  T  val1,    T   val2   )     ;

              bool     operator    >=     (  T  val1,    T   val2   )     ;

              bool     operator    <=     (  T  val1,    T   val2   )     ;

              bool     operator    !=     (  T  val1,    T   val2   )     ;

 

而後, 把  TCompareOperator   做爲 數組 的 泛型約束,

好比,   

class Array1<TCompareOperator   T>

{

              ……

              public void     Sort()

              {

                            ……

              }

}

 

可是這樣的話,   Array1 的 元素 類型 只能是 實現了    TCompareOperator    的 類型  。這就有了侷限,

能夠把  Array1  的 名字 改爲   SortableArray1   ,          哈哈哈哈    。

 

若是要讓 元素 仍然 支持 全部 類型 的話,  能夠 把  Sort() 方法 變成一個 靜態方法,

 

public   static  void   Sort< TCompareOperator  T >( Array1<T>   arr )

 

Template  是 僅用於 值類型 的, 值類型  包括 基礎類型(int float double char 等) 和 結構體,

Interface  則 用於 引用類型(對象),

 

爲了 讓  Sort()   方法 支持  值類型 和 引用類型 的 元素,   還須要 給 泛型約束 加上 引用類型 的 泛型約束 :

 

public   static  void   Sort< TCompareOperator   ICompareOperator     T >( Array1<T>   arr )

 

ICompareOperator   是一個 實現了   > < == != >= <=   運算符 重載  的  interface  ,

除了 interface,  基類 也能夠做爲 引用類型 的 泛型約束,  就是說,  基類 和 Interface 均可以 做爲 引用類型 的 泛型約束  。

結構體 自己 應該也能 做爲 結構體 的 泛型約束,   這 表示 這種 結構體 和 它 的 子類 結構體  能夠 做爲 泛型參數   。

問題是  結構體 能夠繼承,可是 不能 多態,   因此 要 注意 子類 結構體 實際上 被 調用 的 方法 是 哪一個 。

結構體 的 繼承 純粹 是 爲了 複用代碼,  子類結構體 不能 override 基類結構體 的 方法, 可是能夠用 new 覆蓋,

子類 結構體 的 值 和 指針 不能 賦值 給 基類 結構體 的 變量 和 指針變量  。

 

注意,   interface  這裏 只做爲 約束,不會 使用 多態, 由於 具體類型 會經過 泛型參數 T 指定,  因此, Sort() 方法 中 對於 T 對象 的 方法調用 仍然 是 靜態編譯 時 決定的 ,  和 普通 的 調用 對象方法 是 同樣 的 。

除非是 A 類 實現了 I1 和 I2  兩個接口, 這兩個接口 有 同簽名方法 Foo(),  而 Sort() 方法中 須要 調用 Foo(),

這種狀況 就要看 C# 中 對於 這樣的狀況 會不會 報錯 :

 

A a =  new  A()  ;

a.Foo()   ;   

 

A 類 對 I1  I2   兩個接口 的 同簽名方法 Foo()  分別進行了 顯式實現,那麼 在 C# 中上述   a.Foo()  代碼 會不會 報錯?

若是不報錯,那調用的 Foo() 方法 是 哪一個 接口 的 實現 ?

 

等, 咱們在說 D# ,怎麼說到 C# 了?  沒錯,就是說 C#,       看看 C#  怎麼處理 這種狀況 ,

若是 會報錯,  那麼 對於 D# ,   能夠 先把 a 對象 轉型爲 要調用 Foo() 方法 的 那個 接口類型, 好比  I1  a1 =  (I1)a  ;  ,  再傳給 泛型方法,

好比, 有個 方法   void Hello< I1  T >()   ,       那麼 ,

 

A a =  new  A()  ;

I1  a1 =  (I1)a  ; 

Hello<I1>( a1 )   ;           //   這裏將 T 聲明爲 I1,  若是不是這種 顯式實現 多接口 同簽名方法狀況的話

 

看   Hello<I1>( a1 )   ;  ,   這裏將 T 聲明爲 I1,  若是不是這種 顯式實現 多接口 同簽名方法狀況的話,應該是

Hello<A>(a)   ;          將 T 聲明爲 A,

 

而 將 T 聲明爲 I1,    意味着   泛型方法 中 對  a.Foo() 方法 的 調用 就是 使用了 多態  。

 

多態的話,  會在 向上轉型  (I1)a   時 會 查找 虛函數表,   這是 查找一個 Hash 表 ,   因此 多態 比 普通方法 調用 會多這麼一個 性能損耗, 固然找到 虛函數表 後, 還要在 虛函數表 中 查找 函數指針,  這裏會比 普通方法 的 動態連接 多做一個 加法,  固然這個 加法 的 性能開銷 基本能夠忽略  。

 

因此, 可以不使用 多態 的話 ,  就 不要 使用 多態,    固然 這是說 這裏 指定 泛型參數 T 的 場合,不是說 其它 場合,  否則 的話 面向對象 愛好者 可能會 生氣 的  。^^

固然, 從上面舉例能夠看到,  指定 泛型參數 通常狀況 均可以 直接 指定 具體類型  A ,    只有 A 實現了 2 個 或 多個 interface,  這些 interface 又有 同簽名 的 方法,A  對 這些 同簽名 方法 顯式實現 的 狀況 纔會須要將 A 向上轉型 爲 interface,   而後指定 泛型參數 T 爲 interface  。

 

好吧,  其實不用這樣,  就算 顯式實現 多接口 同簽名方法,   其實也不用 向上轉型,  仍是

 

Hello<A>(a)   ;  

 

就能夠 ,  咱們看 方法 定義:

 

void Hello< I1   T >()  

 

這裏的  T  的 約束 是  I1 ,   編譯器 能夠 據此 推斷出 應該調用 A 實現 I1 的 Foo()  方法  ,  因此仍然在 靜態編譯 時 就能夠決定 調用  A 顯式實現 I1 的 Foo()  方法  。

啊哈哈,  其實上面主要是 把 問題分析一下,   而且 把  D# 實現多態 的 原理 再 梳理一下  。

 

另外,還引出一個問題,  Array.Sort()  方法 

 

public   static  void   Sort< TCompareOperator  ICompareOperator  T >( Array1<T>   arr )

 

若是要對 參數 arr 像上面說的那樣 向上轉型 的話, 就 不是   I1 a1 =  (I1) a  ;  

而是     

Array1<A>   arr =  new   Array1<A>( 10 )  ;       //   參數 10 是 數組長度

Array1<I1>   arr1   =   ( Array1<I1> )  arr   ;

 

這就是    Array1<A> 向上轉型爲 Array1<I1>   的 問題 了,  至關於 C# 裏的  A [ ]  轉  I1 [ ],  或者  List<A>  轉  List<I1>   ,

C# 裏 好像 不容許 這樣轉,    但我想 D# 應該容許 。     ~~~

 

 

爲了讓 Sort() 方法 支持 基礎類型 和 自定義 結構體,  基礎類型 固然 會 內置 標記 爲 實現了  TCompareOperator     。

雖然 標記 了 實現  TCompareOperator ,    可是 基礎類型 和 自定義 結構體 運算符 重載 的 實現方式 是不同的,

基礎類型 是 原生 的  C 的 運算符 運算,   而 結構體 的 運算符 重載 是一個 函數調用  。

 

說到 函數調用,會想起 內聯,   別跟我說 內聯,  我不太打算在 D#  裏 支持 內聯,

內聯 是個 複雜的 東東,   是個 麻煩事,

另外,  D#  的 第一目標 是 保持 基礎類型 的 原生效率,    至於 自定義 類型(包括 結構體 和 對象),  調用 函數 的 性能損耗 不算什麼 。

而 現代 CPU 架構 的 寄存器 愈來愈大,     因此 函數調用  入棧出棧 傳參 返回值 的 開銷 應該 愈來愈 稀釋 。

 

這是 運算符 重載 的 方式,  上面說了, 還能夠有  IComparer 的 方式,

好比 ,  聲明一個  IComparer< T>    接口 :

 

interface IComparer<T>

{

            CompareResult     Compare( T  val1 ,   T  val2 )   ;

}

enum CompareResult

{

            Greater,

            Less,

            Equal

}

 

Sort() 方法 聲明爲 

 

class Array1<T>

{

              public void Sort( IComparer<T>   comparer )

              {

                             ……

              }

}

 

能夠看到,   IComparer<T>  的 T 是由 Array1<T>  的  T  決定的,    好比 :

Array1<int>  iArr = new Array1<int>()   ;

 

那麼,  傳給 Sort() 方法 的 IComparer 對象 應該是  IComparer<int>  類型  。

 

基礎類型 也能夠用 IComparer 的方式,  用  IComparer  方式的話,  比較 就是一次 函數調用,  對 基礎類型 來講, 效率 比 直接 用 運算符 的 方式 低一點 。

 

進一步,   還要解決一個問題,   就是  按 值 傳遞 和 按 指針 傳遞 的 結構體 在 泛型 時 調用方法 的 語法 統一 問題  。

 

好比,  咱們寫一個 Hash 表,

 

class   HashTable<THash Object   TKey,     TValue>

{

           public void Add( TKey key,   TValue value )

           {

                       int  hash  =  key.GetHashCode()  ;

                       ……

           }

}

 

能夠看到,   在 Add() 方法 中,   會調用   key.GetHashCode()  方法 來 計算 key 的 hash 值,

TKey 的 泛型約束 是  THash Object,   Object 是有 GetHashCode() 方法 的,  THash 也有 GetHashCode() 方法, 咱們來看看 THash 的 定義:

 

template     THash

{

             int       GetHashCode()     ;

}

 

咱們定義 2 個 結構體,  A 、B  :

 

struct      A      :     THash

{

              public    int   GetHashCode(  A   a  )

              {

                          ……

              }

}

 

struct      B      :     THash

{

              public    int   GetHashCode(  B  *   b  )

              {

                          ……

              }

}

 

A   B    都 實現了 THash 的  GetHashCode()  方法,    A  實現的是 按值傳遞 的 方式, B 實現的是 按指針傳遞 的 方式,

按值傳遞 和 按指針傳遞 可任選一種,只要 實現了一種 就算 實現了  template 的 方法 ,   固然,  2 種 都實現  也能夠 。

那麼,  問題來了,   對於  按指針傳遞  的 方式,  好比

 

HashTable < B *,    string >   ht  =  new  HashTable < B *,    string >()   ;

 

此時,   泛型生成 的 具體類型 是 :

 

class   HashTable~1

{

           public void Add( B *  key,    string value )

           {

                       int  hash  =  key.GetHashCode()  ;

                       ……

           }

}

 

能夠看到,  Add() 方法 中 計算 hash 值 的 代碼 是

 

int  hash  =  key.GetHashCode()  ;

 

key 是 B *  類型, 這裏應該是   key ->  GetHashCode()  ;     而不是   key.GetHashCode()  ;    ,  這樣就出問題了 。

 

那怎麼解決這個問題?  我提出了    .->     操做符,      就是 把   . 和 ->  兩個 操做符 結合到一塊兒  。

.->   操做符 只能用於 泛型類 和 泛型方法 內的 代碼 以及 只能用於 結構體 和 結構體指針 類型  。   當使用   .->   操做符 時,  編譯器 會 判斷 變量 是 結構體 仍是 結構體指針, 若是是 結構體,  則 按   .   操做符 處理,  若是是 結構體指針, 則 按   ->   操做符 處理  。

結構體指針 是指 結構體 的 一階指針,   即  B  *   ,   只有一個 星號 的 指針  ,     若是是 二階 或 多階 指針,好比  B * *  ,    B * * *    ,  這種 「指針的指針」,  則 編譯器 會 報錯  。

 

這樣就很好的實現了 對 結構體 的支持,  這很重要,  這意味着 D# 能夠 支持 高性能 的 底層 代碼,  這能夠有效的 支持 D# 應用於   底層模塊 、 基礎應用 、 高性能中間件 、 遊戲  。       C++  的 不少 應用場合 D# 都能 勝任  。     D#   又叫   D++    。

D#  判斷 是不是  unsafe 代碼 的 標準 是 是否使用了 指針,     使用了 指針 就是 unsafe,  不使用指針 就 不是 unsafe  。

D#  鼓勵 使用 unsafe 代碼  。                

在     底層模塊 、 基礎應用 、 高性能中間件 、 遊戲     裏 都 須要 大量使用 unsafe 代碼  。      不須要 也 不提倡 使用 unsafe 代碼 的 是  業務系統,  可是 業務系統 使用的 各類 基礎庫 內部 應該是 unsafe 代碼 實現的,   好比  集合(List,   Dictionary,   Queue,   Stack,   LinkedList  等) 、  Socket  、  IO(File  等)      等等  。

還有 第三方庫,    事實上 基礎庫 和 第三方庫  沒有 明顯 的 界限,   上面說了,  D# 開放的 「庫」 的 理念,  任何人 均可以 爲   D# / ILBC   寫  庫,

咱們能夠在 必定共識 的 基礎上,  把 某些庫 稱爲  「標準庫」,   但 這也能夠不是必定的,    你搞一個 「A 標準庫」,  別人還能夠搞一個 「B 標準庫」, 還能夠有 「C 標準庫」 、 「D 標準庫」    ……

總之 想怎麼玩 均可以  。

 

咱們能夠再來概括一下  結構體  的 幾種 數據模型 :

1  棧 內 結構體,

2  數組 內 的 結構體, 數組 屬於 堆,  數組 能夠 包含 大量 海量 的 結構體,

3  數組堆

 

爲了 對 第 1 種 模型 ,  棧 內 的 結構體 更好的 支持,  還能夠提供 動態分配 棧 空間 的 特性 。

那 第 3 種 模型 數組堆 又是什麼?    「數組堆」 這個 名字 我 是 想出來的 ,  也不必定 很貼切 。 就是 一次 申請一個 很大 的 byte 數組, 而後在 這個 數組 裏 來 分配空間,   這樣就不用 每次 須要 分配內存 都要 經過 堆  。

這種作法 在 C++ 裏 也許叫作  「內存池」  。

 

想到 數組堆 是 由於 上面 說 基礎應用 的 時候 提到 編譯器,  我想 早期 的 編譯器 可能 會 申請一個 比較大 的 byte 數組, 而後 把 生成的 語法成員樹 保存 在 這個 byte 數組 裏,   每一個 語法成員 是一個 結構體,  每次能夠爲 這個 結構體 在 數組 裏 分配一段空間,把 結構體 保存在 這個 數組 的 一段空間 裏 。

只須要有一個 變量  p  保存 剩餘空間 的 首地址 就能夠了,  每次分配後,  p 的 值 加上 分配的空間 大小 ,  即   p  +=   分配的 Size   ;     ,

這有點相似 棧,  p  就至關於 棧頂 ,   等 ,,,,  好像就是 棧  。

這和 堆 的 區別 和 優勢 是,   數組堆 的 分配 很是快, 就是   p  +=   分配的 Size   ;       這樣一個 加法 和 賦值 運算 。  並且 沒有 堆 的 碎片問題, 包括 從 碎片(空閒空間) 中 尋找 最接近 分配大小 的 碎片 來 分配 的 問題,  以及 從 碎片 中 分配 後 產生 新 的 碎片 等等 複雜 的 堆 管理 問題  。

固然,相對的, 數組堆 就 沒有 堆 那麼 靈活  。   由於 數組堆 回收 空間 一般 時 整個 數組 所有回收,  也就是   p  =  數組首地址   ;    ,

能夠 在 一個 任務 或者 請求 內 使用 數組堆,  好比 每次編譯 使用一個 數組堆, 編譯的結果(語法成員樹) 就 存放在 這個 數組堆 裏,   編譯過程 中 產生的 表示 字符數組段 的 Span 結構體 也能夠 放在 數組堆 裏,  固然 能夠 放在 另外一個 數組堆,    語法成員 和 Span  各自 使用 一個  數組堆  。   這樣 每次 編譯 2 個 數組堆 搞定 。

若是 涉及 到 併發,  即 多個   任務 / 請求 / 線程   之間 須要 共享數據,  那 共享數據 不適合用 數組堆, 應該 直接 使用 堆  。

大概 是 這樣  。

 

不考慮 併發 的話,  就 不須要有 lock,   這樣, 從 數組堆 裏 分配 就是一個 加法賦值  p  +=   分配的 Size   ;   ,  清空 數組堆 就是 一個 賦值  p  =  數組首地址   ;  ,  這個速度 比起 堆 的 分配 和 回收 ,   簡直  快到飛起  。

 

接下來該說什麼了? 說說 Attribute,   說實在的, 真不想在 D# 裏 支持 Attribute, 這會讓 編譯器 變得 複雜,  另外, 我是個沒有耐心的人, 宏偉計劃 要 作 的 事 愈來愈多 不免讓人 心煩  。

Attribute  在 開發實踐 中 最大的 用處 大概 是 ORM, 用於 描述 數據庫   表 - 類   列 - 屬性   的 對應關係 。 對於 D# 而言,  Attribute 最大的用處 是   [ DllImport( ) ]  Attribute ,  這個 用於 描述 對 外部庫 函數 的 調用, 這很重要 。   

 

調用 外部庫 是 ILBC / D# 的一個 核心特性 模塊 基因 。

 

用 Attribute 描述  外部庫 函數  確實 很方便,   比 關鍵字 的 描述能力 強不少,  並且 不須要 語言 增長 關鍵字 來支持 。

 

.Net / C#   內部 用 Attribute 倒用的很 嗨, 不少 與 開發者 無關 的  JIT / CLR 特性 都是用 Attribute 描述的 。

可是咱們再看看 Ruby 和 Javascript ,   Ruby 有 Attribute  嗎?   Ruby on Rail 好像玩的很溜,     Javascript 有 Attribute 嗎?   Javascript 的 動態特性 是 最吸引人的一個特性 。

 

不過總的來講,   Attribute 好像仍是得支持一下,  畢竟 一個 完備的 元數據 體系 仍是須要的 。

支持 Attribute 也不難,   編譯器 加入 對 Attribute 的 支持 也能夠保持 架構 的 簡潔, 這問題不大,  ILBC Runtime 對 Attribute 的 支持 也 不難, 就是個 工做量 的 問題,  由於 ILBC 原本就有 元數據系統,  這是 動態連接 和 反射 的 基礎,   ILBC 動態連接 自己 就是 基於 反射 的,  反射 基於 Hash 表 查找, 固然 Hash 表 和 鏈表 能夠共存,  就是說 成員(類 字段 方法) 同時保存在 Hash 表 和 鏈表 裏, Hash 表 用於 按 名字 查找, 鏈表 用於 遍歷  。

Attribute  只不過是 本來 類 字段 方法 的 元數據 上 增長一些 屬性, 或者說 增長一個 屬性集合  。

 

再來看看 StackTrace,  說實在的,  StackTrace 我也不想支持,  支持 StackTrace 是個 麻煩 事,  估計要作比較多的工做,雖然 提及來 兩句話 就能說清楚, 好比 「在 編譯 的 時候 在 每一個 方法調用 的 時候 生成一段 代碼 用於 記錄 StackTrace」 ,  提及來簡單,  但 估計 也很煩, 主要 是 咱們 沒有那麼多時間和精力,  這很容易分散咱們的時間和精力 。

並且,   能夠看到,  StackTrace 會 在 函數調用 時 作一些 額外的工做,因此是會產生 性能損耗 的。

可是, 若是不支持 StackTrace, 會不會像 C / C++ 那樣 太原始 太裸  ?

不過咱們 看看 Chrome 裏的 javascript 報錯時 有沒有 StackTrace ?   若是沒有,那 D# 也能夠沒有 。  ^^

 

ILBC / D# 應該是一個 能夠像 腳本 同樣 容易的使用 的 AOT / Native  語言平臺 。   就像  Python ,  Javascript  。  固然 ILBC 是  AOT / Native  的 。

可是 從 使用上來講,  ILBC / D# 應該能夠像 腳本 同樣 容易的 使用,    就像  Python ,  Javascript  。

 

固然,你能夠用 ILBC / D# 開發出各類類型和 風格 的 語言,好比   AOT, JIT 腳本, 面向對象, 函數式    等等  。

 

D# 應該用 動態編譯 D# 代碼 取代 C# 的 Emit 和 表達式樹 。

C# 的 Emit 和 Expression    是 笨重 的 堆砌  。

 

協程 是 ILBC / D# 的 一個 核心特性 模塊 基因 。

有關 協程, 見《協程 的 實現方法》   http://www.javashuo.com/article/p-vojdpflm-be.html    。

 

再談談 調試 的 問題,     調試, 是 IDE 的 部分,  做爲一個 開放 自由 有生命力 的 語言平臺,  是不該該 依賴於  IDE  的,

咱們 歡迎 IDE 提供好的支持,   可是 語言平臺 不該該 依賴於  IDE   。

看看  宇宙第一 IDE 和 C#  的 關係 就知道了,   離開 Visual Studio ,    怎麼開發 .Net 程序?  這不可想象 。

這不只僅 是 對 C# 語法 的 語法錯誤 和 智能提示 的 支持,    還包括 對 具體 的 程序項目 的 支持,

好比,   WinForm 程序,     沒有  Visual Studio ,你怎麼寫?

Asp.net 程序,   沒有 Visual Studio ,   你怎麼寫?

並且 Visual Studio 的 WinForm ,   Asp.net  項目 拿給 VsCode 是不能直接編譯的,  這是我猜的, 由於我沒怎麼用過 VsCode  。

這些現象,   表示 這不是 程序員 要的 「理想國」    。

 

ILBC    要實現的,是一個 用 記事本 也能寫 程序 的 語言平臺,    這是 程序員 的 理想國  。

這其實 很簡單,  咱們只須要一些 簡單 的 規則 就能夠實現,  好比,  最簡單的, 編譯器 是一個 命令,咱們能夠告訴 編譯器 一個 路徑, 這個 路徑 就是 項目的根目錄,  編譯器 會 尋找 這個 目錄 和 全部 的 子目錄 裏的 源代碼 文件 進行編譯,   那麼 對於 Bin 目錄, 或者 資源目錄 等等一些 咱們 須要 編譯器 忽略 的 目錄 怎麼辦?

能夠相似 Git,   在  項目目錄 下 放一個  ilbc_src.ignore   的 文件,   裏面聲明 須要 忽略 的 目錄,  就能夠了  。

 

甚至,  能夠比 Git  還簡單,   ilbc_src.ignore   只容許 聲明 項目目錄 下 的 第一級 子目錄,  這樣 就 太簡單了  。

實際上,   這也夠用了  。

 

編譯器 對 項目目錄 下的 源文件 編譯,  會把 全部的錯誤 都在 控制檯 裏 列出來,  哪一個位置是什麼錯, 這和 Visual Studio 或者 其它 IDE 是同樣的 。

 

對於 像   WPF,  Asp.net    這種類型 的 項目,  有 Xml 格式 的 前端代碼(文件),   這也沒問題,   你能夠用 Xml 編輯器 來寫 前端代碼(文件),  固然, 用 記事本 也能夠  。  ^^

而後,   編譯器 一樣 對 項目目錄 下 全部的 源代碼文件, 包括 前端文件 和 後端文件 進行編譯 ,   並顯示 全部錯誤  。

 

因此,   無論 後端代碼 仍是 前端代碼 ,   你能夠選擇 任意的 文本編輯器 來 編寫,  而後 交給 編譯器 來編譯  。

你也能夠 根據上述 規則 開發一個   IDE  ,     這均可以  。

 

你的項目 拿給 別人,     別人 能夠用 本身的 文本編輯器 和 編譯器 來 繼續 開發 和 編譯,也能夠用 IDE  。

 

在這方面,    Chrome   彷佛 乾的不錯,    你能夠用 任意 的 文本編輯器 寫 Javascript,   而後 Chrome 的 DevTools 可讓你很方便 的 調試程序 。

相比之下,     Visual Studio  在 不一樣 版本間  都 可能 不支持 互相 打開項目  。

 

等,  咱們是說 調試 的,  怎麼說着說着 就 跑題了 ?      調試 是一個 麻煩事,   原理 大概 是這樣:

首先,要 調用一個 操做系統 提供的 API  「附加進程」,   調用了這個 API 就能夠 把 系統的 調試器 附加 到 要調試的 進程,  以後 進程 每執行一條指令 大概都會發起一箇中斷,  系統會在這個中斷裏 調用 咱們的 處理程序,   咱們的 處理程序 能夠知道 當前是 在 執行 第幾行 的 反彙編代碼,   根據 彙編代碼 的 行數 和 指令 去 「符號表」 中 尋找 這一行 對應的 源代碼 是 哪一行,  而且 指令 也要 和 符號表 中 對應的 源代碼行 的 指令 符合,   這樣就能夠 單步調試 了  。

符號表 就是 好比 .Net 裏的 pdb 文件,   在 編譯時 會 生成 彙編代碼 和 源代碼 的 對照表, 這個 對照表 就是 符號表 。

固然,以上 這些 是 我 猜 的 。 

 

這些 實際上 作起來 很麻煩,    並且 這些是 最基本,  通常 調試 除了  單步調試,  還有 Step Over Function,    Step Out Function,    直接運行到下一個斷點,  還有 查看 局部變量 全局變量 。

 

因此, 我建議,  沒必要將 精力 放在 實現 調試 上, 可是, 應該提供 控制檯 輸出 的 支持 。

控制檯輸出  是  最古老 的 調試方式, 也是  永不過期  的 調試方法  。

 

數組堆

 

template interface

值類型 結構體 結構體 方法, 傳值, 傳指針, 泛型, 數組 ,集合類泛型,  .->  操做符,  > < >= <=  == 運算符重載, Comparer,

棧 結構體, 對象圖,

底層應用,基礎應用,    圖形圖像,2D 3D,遊戲,  視頻音頻, 視頻採樣,音頻採樣,人工智能,大數據,數據庫,文檔數據庫,大規模並行計算文檔數據庫,搜索引擎,爬蟲,編譯器  , 文本編輯器,  大型文本編輯器    。

Attribute,   

調用 外部庫 是 ILBC / D# 的一個 核心特性 模塊 基因 。

StackTrace,

ILBC / D# 應該是一個 能夠像 腳本 同樣 容易的使用 的 AOT / Native  語言平臺 。

固然,你能夠用 ILBC / D# 開發出各類類型和 風格 的 語言,好比 AOT JIT 腳本,面向對象,函數式 等等 。

 

D# 用 動態編譯 D# 代碼 取代 C# 的 Emit 和 表達式樹 。

 

調試

 

ILBC    源代碼 項目 規範 

 

協程 是 ILBC / D# 的 一個 核心特性 模塊 基因 。

 

template 和 interface   同時做爲一等公民

D# 是程序員的語言,不須要太多包裝和修飾,

D# 是 簡單的, 編譯器 和 IDE 都是,    程序員 的 語言 是 簡單的

 

let  , 相似 js 的 let,     用以 支持 完備 的 閉包 特性,通常 的 變量 至關於 js 的 var ,  加上 let 至關於 js 的 let,好比 let string  s  ;

能夠起到 在 循環 中 給 閉包 傳遞 變量 的 做用,

 

Task 庫

 

返回多個 返回值,好比 D# 中

 

public (int i,  string s)  Foo()

{

        int i;

        string s;

        ……           //  對  i, s  操做

 

        return  (i, s);

}

 

var r = Foo();

r.i   ……       //  r.i  表示返回的  i

r.s  ……       //  r.s 表示返回的  s

 

編譯爲 InnerC 是這樣:

 

struct R<>1

{

         int  i

         string  s,

}

 

Foo()

{

         R<>1   r  ;

         r.i     ……       // 對  i  操做

         r.s    ……       // 對  s  操做

         ……

         return   r  ;

}

 

R<>1  r  =  Foo()  ;

r.i   ……       //  r.i  表示返回的  i

r.s  ……       //  r.s 表示返回的  s

 

D#  是否 要 支持  async await 語法?

我曾經想過用    閉包 + 觀察者 來 取代   async + 職責鏈,   但 後來 發現,   閉包 和 職責鏈  好像 更配,這 有點諷刺 ……

但 其實 閉包 + 觀察者  也是 能夠的, 好比 Cef 中  ResourceHandler  的 方法裏 能夠    return true / false ;   和   callback.Continue()   ;

若是 當前 就處理完了 任務,  則 能夠直接 return true / false,   告知 Cef  能夠進行下一個任務,

若是 當前 任務 異步 執行,   則 能夠 等 異步任務 完成時 調用   callback.Continue()   告知 Cef  能夠進行下一個任務 。

 

今天看到 羣 裏 有 網友介紹 kooboo json  使用 表達式樹 提升效率 ,  https://github.com/Kooboo/Json/blob/master/Kooboo.Json/Formatter/Deserializer/Expression/CollectionBuild.cs   

這樣固然很好,  可是我仍是以爲 表達式樹 太複雜 了,我以爲應該寫 C# 代碼 來 動態編譯 就能夠  。

好比

 

「 obj.name = dataRead [ \「 name \」 ] ; 」

 

這是 ORM 裏 給 Entity 屬性 賦值 的 代碼,   這能夠算是  C#  腳本  。


這種作法 只是 第一次 編譯時 可能 比 表達式樹 多一點時間 ,  編譯完成後 運行效率 是同樣的 。

 

其實 第一次 編譯時 也多不了 多少時間,   只是 多了 文本語法分析 的 時間 。

 

之後  D# 也要這麼幹, 提供 動態編譯,支持  D# 腳本 。

D# 是 不會 提供 C# 那樣的 表達式樹 的 。

 

C# 的 表達式樹 就是 面向對象 氾濫 的 結果,   認爲何均可以用 對象 來表達,

其實 字符串 表達的會更好 。

 

在 D# 看來,  C# 的 Emit 和 Expression    是沒有意義的堆砌  。

在 D# 腳本 看來,  C# 的 Emit 和 Expression    是 不須要 的   。

相關文章
相關標籤/搜索