這篇文章 隨意 的 記錄 關於 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 是 不須要 的 。