簡體   English   中英

C#中可空類型的替代方案

[英]Alternatives to nullable types in C#

我正在編寫適用於一系列數字數據的算法,有時,系列中的值必須為null。 但是,由於此應用程序對性能至關重要,因此我避免使用可空類型。 我已經對這些算法進行了性能測試,專門比較了使用可空類型和非可空類型的性能,在最好的情況下,可空類型的速度慢了2倍,但往往差得多。

最常用的數據類型是double,目前選擇null的替代方法是double.NaN。 但是我知道這不是NaN值的確切用途,所以我不確定是否有任何問題,我無法預見,最佳做法是什么。

我有興趣找出以下數據類型的最佳空替代品:double / float,decimal,DateTime,int / long(盡管其他數據類型非常受歡迎)

編輯:我想我需要澄清我對性能的要求。 數字數據的演出通過這些算法在幾個小時的時間內處理。 因此,雖然例如10毫秒或20毫秒之間的差異通常是微不足道的,但在這種情況下,它確實對所花費的時間產生了重大影響。

好吧,如果你已經排除了Nullable<T> ,那么你將留下域名值 - 即你認為是空值的幻數。 雖然這並不理想 ,但這種情況並不少見 - 例如,許多主框架代碼將DateTime.MinValue視為null。 這至少使損害遠離共同的價值......

編輯以僅突出顯示沒有NaN的位置

所以在沒有NaN ,也許可以使用.MinValue - 但是請記住,如果你不小心使用相同的值意味着相同的數字,會發生什么樣的弊端......

顯然,對於未簽名的數據,您需要.MaxValue (避免零!!!)。

就個人而言,我嘗試使用Nullable<T>來更安全地表達我的意圖......也許有可能有方法來優化你的Nullable<T>代碼。 而且 - 當你在所有需要的位置檢查神奇數字時,也許它不會比Nullable<T>快得多?

在這個特定的邊緣情況下,我有點不同意Gravell:Null-ed變量被認為是“未定義”,它沒有值。 因此,無論用什么信號都可以:即使是魔術數字,但是你必須考慮到一個神奇的數字在將來突然成為一個“有效”值時會困擾你。 使用Double.NaN,您不必為此擔心:它永遠不會成為有效的雙倍。 但是,您必須考慮到雙精度序列意義上的NaN只能用作“未定義”的標記,顯然,您也不能將它用作序列中的錯誤代碼。

因此無論用什么標記'未定義':必須在值集的上下文中清楚地表明該特定值被認為是'未定義'的值,並且將來不會改變。

如果Nullable給你帶來太多麻煩,可以使用NaN或其他任何東西,只要你考慮后果:選擇的值代表'undefined'並且將保留。

我正在開發一個使用NaN作為null值的大型項目。 我對此並不十分滿意 - 出於與你類似的原因:不知道會出現什么問題。 到目前為止,我們還沒有遇到任何實際問題,但請注意以下事項:

NaN算術 - 雖然大多數時候,“NaN促銷”是一件好事,但它可能並不總是你所期望的。

比較 - 如果你想讓NaN比較相等,價值的比較會變得相當昂貴。 現在,測試浮點數是否相等並不簡單,但排序(a <b)可能會變得非常難看,因為nan有時需要更小,有時需要大於正常值。

代碼感染 - 我看到許多算術代碼需要特定處理NaN才是正確的。 因此,出於性能原因,您最終會得到“接受NaN的功能”和“不接受NaN的功能”。

其他非有限的 NaN是唯一的非有限值。 應該牢記......

禁用時, 浮點異常不是問題。 直到某人啟用它們。 真實故事:ActiveX控件中NaN的靜態初始化。 聽起來不可怕,直到你改變安裝使用InnoSetup,它使用Pascal / Delphi(?)核心,默認情況下啟用了FPU異常。 我花了一段時間才弄明白。

所以,總而言之,沒有什么是嚴重的,盡管我不想經常考慮NaNs。


我會盡可能經常使用Nullable類型,除非它們(已證明是)性能/資源約束。 一種情況可能是具有偶然NaN的大型矢量/矩陣,或者大型命名的單個值,其中默認的NaN行為是正確的


或者,您可以使用矢量和矩陣的索引向量,標准“稀疏矩陣”實現或單獨的bool /位向量。

部分答案:

Float和Double提供NaN(非數字)。 NaN有點棘手,因為根據規格,NaN!= NaN。 如果你想知道一個數字是否是NaN,你需要使用Double.IsNaN()。

另請參見二進制浮點和.NET

通過定義自己的結構,可以避免與Nullable<T>相關的一些性能下降

struct MaybeValid<T>
{
    public bool isValue;
    public T Value;
}

如果需要,可以定義構造函數或從TMaybeValid<T>等的轉換運算符,但是過度使用這些東西可能會產生次優性能。 如果避免不必要的數據復制,暴露場結構可以是有效的。 有些人可能會對外露場的概念不以為然,但它們可以大大提高性能。 如果將返回的函數T將需要具有類型的變量T保持其返回值,使用MaybeValid<Foo>簡單地通過增加4要返回的事情的大小。 相比之下,使用Nullable<Foo>將要求函數首先計算Foo ,然后將其副本傳遞給Nullable<Foo>的構造函數。 此外,返回Nullable<Foo>將要求任何想要使用返回值的代碼必須至少為Foo類型的存儲位置(變量或臨時)創建一個額外的副本,然后才能對其執行任何有用的操作。 相比之下,代碼可以使用Foo類型變量的Value字段與任何其他變量一樣高效。

當調用Nullable的一個成員或屬性(拳擊)時,可能會發生顯着的性能下降。

嘗試使用帶有double +布爾值的結構來告知是否指定了值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM