Решить "Тип значения, допускающий значение NULL, может быть нулевым" для values1.Count в NetCore 6
Использование NetCore 6 и
<Nullable>enable</Nullable>
по определению проекта у меня есть:
protected Boolean CrossBelow<T>(IList<Nullable<T>> values1, IList<Nullable<T>> values2) where T : struct, IComparable {
if (values1[values1.Count - 2] == null || values1.Last() == null || values2[values1.Count - 2] == null || values2.Last() == null)
return false;
return values1[values1.Count - 2].Value.CompareTo(values2[values2.Count - 2].Value) > 0 && values1.Last().Value.CompareTo(values2.Last().Value) < 0;
}
Но я получаю предупреждение:
Nullable value type may be null
В нескольких местах, например:
values1[values1.Count - 2]
Я могу решить часть этого, используя:
values1?[values1.Count - 2]
Но я получаю ошибку компиляции
`cannot convert from 'int?' to 'int'`
При попытке сделать то же самое с values1.Count:
values1?[values1?.Count - 2]
Как это решить?
2 ответа
Вещи как:
values1[values1.Count - 2]
или же
values1.Last();
в основном являются вызовами функций, и компилятор не может быть уверен, что они производят те же значения при последующих вызовах (скажем, в первый раз, когда вы вызываете
Last()
он может вернуть не нуль, но во второй раз он вернется
null
, компилятор не может знать, что в вашем случае это невозможно). Просто введите переменные, это не помешает и без этих предупреждений:
protected Boolean CrossBelow<T>(IList<Nullable<T>> values1, IList<Nullable<T>> values2) where T : struct, IComparable
{
var v1Prev = values1[values1.Count - 2];
var v1Last = values1.Last();
var v2Prev = values2[values2.Count - 2];
var v2Last = values2.Last();
if (v1Prev == null || v1Last == null || v2Prev == null || v2Last == null)
return false;
return v1Prev.Value.CompareTo(v2Prev.Value) > 0 && v1Last.Value.CompareTo(v2Last.Value) < 0;
}
Проблема в том, что вы напрямую обращаетесь к
Value
свойство . Компилятор не знает, могут ли измениться значения в вашем списке в последнем и предпоследнем индексе между вашими проверками null и когда вы обращаетесь к этим элементам в операторе return.
Самое простое решение здесь — использовать условный оператор с нулевым значением . Это позволит вам даже удалить явные проверки null, так как теперь все происходит за один раз:
protected bool CrossBelow<T>(IList<T?> values1, IList<T?> values2) where T : struct, IComparable =>
values1[^2]?.CompareTo(values2[^2]) > 0
&& values1[^1]?.CompareTo(values2[^1]) < 0;
Я также упростил доступ к массиву с помощью индексов .
Так что же происходит в приведенном выше коде?
Ваш метод занимает два
ILists
а также
values2
которые содержат значения
T?
(что то же самое, что
Nullable<T>
только короче). Обратите внимание, что
IList
сами экземпляры не могут быть нулевыми (или, по крайней мере, мы не ожидаем, что они будут). Поэтому нам не нужны нулевые условия после самих списков. Итак, после
values1
в
values1?[values1.Count - 2]
не требуется. Причина, по которой вы получали предупреждения, заключается в том, что элемент, возвращаемый операцией индекса, может быть нулевым. Поэтому мы применяем
?
оператор после того , как мы проиндексировали нужный элемент.
Благодаря
?.
выполнение оператора теперь продолжается только до оператора, если значение, возвращенное предыдущей операцией индексирования, не равно нулю. В качестве
CompareTo(object? obj)
в любом случае принимает значение, допускающее значение NULL, нам не нужна проверка null для сравнения. Если значение, возвращаемое
values1[^2]
(доступ к предпоследнему элементу) были нулевыми, тогда
CompareTo()
не будет оцениваться и
null > 0
всегда будет оцениваться как false для
int?
. Вот почему нам не нужны явные проверки null в операторе if.
Вторая строка делает то же самое. Обратите внимание, что любое сравнение
int
с
null
вернется
false
. Так
null < 0
является ложным.