Подтвердить что ты не робот

Альтернативы nullable типам в С#

Я пишу алгоритмы, которые работают над серией числовых данных, где иногда значение в серии должно быть нулевым. Однако, поскольку это приложение критично критично, я избежал использования типов с возможностью NULL. Я имею первичную проверку алгоритмов, чтобы конкретно сравнить производительность использования типов с нулевым значением по сравнению с типами, не допускающими нулевое значение, и в наилучшем случае типы с нулевым значением являются 2x медленнее, но часто намного хуже.

Наиболее часто используемый тип данных является двойным, и в настоящее время выбранная альтернатива null равна double.NaN. Однако я понимаю, что это не точное назначение для значения NaN, поэтому я не уверен, есть ли какие-либо проблемы с этим, которые я не могу предвидеть и какова наилучшая практика.

Мне интересно узнать, какие наилучшие альтернативы null для следующих типов данных в частности: double/float, decimal, DateTime, int/long (хотя другие более чем приветствуются)

Изменить: Я думаю, мне нужно уточнить мои требования к производительности. Концерты числовых данных обрабатываются с помощью этих алгоритмов в то время, которое занимает несколько часов. Поэтому, хотя разница между, например, 10 мс или 20 мс обычно незначителен, в этом случае она действительно оказывает значительное влияние на время.

4b9b3361

Ответ 1

Ну, если вы исключили Nullable<T>, вы останетесь с значениями домена, т.е. магическим числом, которое вы считаете нулевым. Хотя это и не идеально, это тоже не редкость - например, многие основные кодеки инфраструктуры трактуют DateTime.MinValue так же, как и нуль. Это по крайней мере переносит урон, далекий от общих значений...

изменить, чтобы выделить только там, где нет NaN

Итак, где нет NaN, возможно, используйте .MinValue, но просто помните, что происходит, если вы случайно используете то же самое значение, означающее одно и то же число...

Очевидно, что для неподписанных данных вам потребуется .MaxValue (избегать нуля!!!).

Лично я бы попытался использовать Nullable<T> как более безопасное выражение моего намерения... возможно, могут быть способы оптимизации вашего кода Nullable<T>. А также - к тому моменту, когда вы проверили магическое число во всех местах, где вам нужно, возможно, это будет не намного быстрее, чем Nullable<T>?

Ответ 2

Я несколько не согласен с Gravell в этом конкретном случае края: переменная Null-ed считается "не определена", она не имеет значения. Так что все, что используется, чтобы сигнализировать, что все в порядке: даже магические числа, но с магическими номерами вы должны учитывать, что волшебное число всегда будет преследовать вас в будущем, когда оно станет "действительным" значением внезапно. С Double.NaN вам не нужно бояться за это: он никогда не станет действительным двойником. Хотя, вы должны учитывать, что NaN в смысле последовательности удвоений можно использовать только как маркер для "не определено", вы также не можете использовать его как код ошибки в последовательностях.

Итак, все, что используется для отметки 'undefined': в контексте набора значений должно быть ясно, что это конкретное значение считается значением для 'undefined' И это не изменится в будущее.

Если Nullable дает вам слишком много проблем, используйте NaN или что-то еще, если вы рассматриваете последствия: выбранное значение представляет "undefined", и оно останется.

Ответ 3

Я работаю над большим проектом, который использует NaN как значение null. Мне это не совсем удобно - по тем же причинам, что и вы: не зная, что может пойти не так. До сих пор мы не сталкивались с какими-либо реальными проблемами, но имейте в виду следующее:

Арифметика NaN. Хотя большую часть времени "продвижение NaN" - это хорошо, это может быть не всегда так, как вы ожидаете.

Сравнение. Сравнение значений становится довольно дорогостоящим, если вы хотите сравнить NaN с равными. Теперь тестирование float для равенства в любом случае непросто, но упорядочение (a < b) может стать действительно уродливым, потому что nan иногда должно быть меньше, иногда больше нормальных значений.

Инфекция кода. Я вижу много арифметического кода, который требует правильной обработки NaN. Таким образом, вы получаете "функции, которые принимают NaN" и "функции, которые не работают" по соображениям производительности.

Другие non-finites NaN не является единственным не конечным значением. Следует иметь в виду...

Исключения с плавающей запятой не являются проблемой при отключении. Пока кто-то не позволит им. Истинная история: статическая инициализация NaN в элементе управления ActiveX. Не звучит страшно, пока вы не измените установку на использование InnoSetup, которая использует ядро ​​Pascal/Delphi (?), Которое по умолчанию имеет исключения FPU. Понадобился время, чтобы понять.

Итак, в целом, ничего серьезного, хотя я бы предпочел не часто рассматривать NaNs.


Я бы использовал типы Nullable как можно чаще, если только они не являются (доказано) ограничениями производительности /ressource. Один случай может быть большими векторами/матрицами со случайными NaN или большими наборами именованных индивидуальных значений, где правильное поведение NaN правильное.


В качестве альтернативы вы можете использовать индексный вектор для векторов и матриц, стандартные реализации "разреженной матрицы" или отдельный вектор bool/bit.

Ответ 4

Частичный ответ:

Float и Double предоставляют NaN (не номер). NaN немного сложнее, так как, согласно спецификации, NaN!= NaN. Если вы хотите узнать, является ли число NaN, вам нужно использовать Double.IsNaN().

См. также Двоичная с плавающей запятой и .NET.

Ответ 5

Возможно, значительное снижение производительности происходит при вызове одного из Nullable членов или свойств (бокс).

Попробуйте использовать struct с double + boolean, указав, указано это значение или нет.

Ответ 6

Можно избежать некоторых ухудшений производительности, связанных с Nullable<T>, указав свою собственную структуру

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

При желании можно определить конструктор или оператор преобразования от T до MaybeValid<T> и т.д., но чрезмерное использование таких вещей может привести к неоптимальной производительности. Структуры открытого поля могут быть эффективными, если вы избегаете ненужного копирования данных. Некоторые люди могут недооценивать понятие открытых полей, но они могут быть значительно более эффективными, чем свойства. Если функция, возвращающая T, должна иметь переменную типа T, чтобы удерживать ее возвращаемое значение, используя MaybeValid<Foo> просто увеличивает на 4 размер вещи, которую нужно вернуть. В отличие от этого, используя Nullable<Foo>, требуется, чтобы функция сначала вычислила Foo, а затем передала ее копию конструктору для Nullable<Foo>. Кроме того, для возврата Nullable<Foo> требуется, чтобы любой код, который хочет использовать возвращаемое значение, должен сделать по крайней мере одну дополнительную копию в хранилище (переменную или временную) типа Foo, прежде чем он сможет сделать что-нибудь полезное с ним. Напротив, код может использовать поле Value переменной типа Foo примерно так же эффективно, как и любую другую переменную.