我正在尋找一種方法來編譯C#編程語言的時間斷言,比如由C++的BOOST庫或新的C++ 0x標準提供的編譯時斷言。C#能提供一個static_assert嗎?
我的問題是雙重的;這可以在標準的,可移植的C#中實現;另外,行爲是否可以通過給定C#編譯器怪癖的不可移植假設來實現?
在google上快速搜索,發現以下鏈接指向一個technique,它的標準符合性和兼容性我不確定。
我正在尋找一種方法來編譯C#編程語言的時間斷言,比如由C++的BOOST庫或新的C++ 0x標準提供的編譯時斷言。C#能提供一個static_assert嗎?
我的問題是雙重的;這可以在標準的,可移植的C#中實現;另外,行爲是否可以通過給定C#編譯器怪癖的不可移植假設來實現?
在google上快速搜索,發現以下鏈接指向一個technique,它的標準符合性和兼容性我不確定。
該代碼是標準的C#代碼。它可以在任何編譯器上工作 - 但不一定在編譯時。由於在編譯時的評估只有在條件中使用的參數不變的情況下才有可能,所以我認爲優化依賴於編譯器供應商/編譯器開關。
在C++中,靜態斷言既可以是標準(C++ 0x)的一部分,也可以是編譯時需要評估的模板,因此它們可以保證斷言。爲了測試可移植性,我會使用不同的編譯器,尤其是沒有任何優化,否則在程序啓動時可能會遇到異常。從您提供的鏈接
代碼將類似這樣的結構被編譯:
byte a;
if (RenderQuality.Low < RenderQuality.Medium)
a = 0;
else a = -1;
因此,編譯器將拋出一個錯誤。 Trinary(條件)運算符?:不過是語法糖,如'??'運營商。
在c#中沒有靜態斷言,因爲它沒有模板(泛型看起來與模板類似,但有很多不同之處,它們不如模板強大)。一些功能可以使用條件語句和預處理器定義來實現。有關詳細信息,請參閱this topic。
其實,當我改變不能在編譯時進行評估(因此摺疊)表達的東西,我得到一個不同的異常:
無法隱式轉換類型「詮釋」 爲「字節」 。一個明確的轉換 存在(您是否缺少演員?)
不做摺疊的編譯器應該會產生類似的編譯錯誤。因此,在這樣的編譯器中,只有在取出所有斷言之前,它纔會編譯。並不美觀,但它可能更適合在運行時獲得驚喜。
Code Contracts將被添加到C#4.0中。它本質上是以一種優雅的方式完成的相同想法。
如果您不能等待C#4.0,那麼您總是可以在此處獲得Microsoft Research版本的代碼合同: http://research.microsoft.com/en-us/projects/contracts/ – TheMissingLINQ 2009-06-08 13:01:16
您可以嘗試在理想情況下的一些編譯器在需要評估編譯原因(例如,一些屬性控制編制,也許結構佈局)的值使用類似
(0/(condition ? 1 : 0))
。
編輯:因爲屬性參數必須是常量表達式,我假定它們總是在編譯過程中進行評估,所以任何屬性可能就足夠了。
EDIT2: 另一種方法是創建一個自定義屬性(例如StaticCheck),它可以使用一個布爾值,也可以是作爲構建後事件運行的字符串和工具,並使用反射來檢查所有這些屬性。不如直接支持,但比分裂黑客更清潔。
裏面有一個返回值,下面的技巧/黑客作品的功能(至少與Visual Studio 2005,還沒有檢查其他平臺上):
Something Foo()
{
if (compiletime_const_condition)
{
// ...
return something;
}
// no return statement at all
}
這是不漂亮,但它的迄今爲止我的最佳解決方案。
該方法與沒有內置靜態斷言(Delphi,較舊的C++等)的其他語言相同:找到一種機制將條件轉換爲編譯器不滿意的內容條件是錯誤的。
對於C#而言,最容易利用的機制之一是將負文字/常量賦值給無符號類型的警告。這已經在本次討論的某處(或者至少從這裏鏈接的一頁中)被提及過,但值得以純粹的形式展示它。
這裏的守衛兩個常數的示例 - 被編輯到違反的預期值MODULUS_32
和MAX_N
:
const uint _0 = (ulong)MODULUS_32 * MODULUS_32 == MAX_N ? 0 : -666;
-666
的使錯誤消息識別爲由於靜態斷言。在這種情況下使用三元運算符是可取的,因爲這可以更容易地識別正在發生的事情(計算中的負面結果更可能歸因於錯誤,而不是明確的,有意的分配)。命名像_MAX_N_must_be_the_square_of_MODULUS_32
這樣的常量使事情變得更加明確。
這種'靜態斷言'可靠地停止編譯 - 這不只是一些警告,如果有人篡改/warnaserror
開關,可能會丟失。
在某些範圍 - 例如,功能內 - 它可以是需要抑制「未使用的值」經由一個編譯指示警告:
#pragma warning disable 219
const uint _0 = (ulong)MODULUS_32 * MODULUS_32 == MAX_N ? 0 : -666;
#pragma warning restore 219
C#是非常像Delphi的,它缺乏一個預處理器,這意味着靜態聲明和它們的機制不能被打包成整潔的宏 - 如果你想使用它們,那麼你必須在那裏和那裏輸入所有的管道。但就像在Delphi中一樣,讓編譯器在編譯時檢查事情的好處是值得的小事。
在C#中只需要一個static_assert。 – 2009-06-08 09:31:10
我不同意,一個有價值的用例正在驗證編譯時計算的正確性,而不必運行該程序。 – grrussel 2009-06-08 09:48:16