Назначение условного оператора с Nullable ‹value› типами?

EmployeeNumber =
string.IsNullOrEmpty(employeeNumberTextBox.Text)
    ? null
    : Convert.ToInt32(employeeNumberTextBox.Text),

Я часто обнаруживаю, что хочу делать такие вещи (EmployeeNumber - это Nullable<int>, поскольку это свойство объекта dbml LINQ-to-SQL, в котором столбец допускает значения NULL). К сожалению, компилятор считает, что

Нет неявного преобразования между 'null' и 'int'

даже если оба типа будут действительны в операции присваивания обнуляемому int сами по себе.

Насколько я понимаю, использование оператора объединения с нулевым значением не вариант из-за встроенного преобразования, которое должно произойти в строке .Text, если она не равна нулю.

Насколько мне известно, единственный способ сделать это - использовать оператор if и / или назначить его в два этапа. В данном конкретном случае меня это очень расстраивает, потому что я хотел использовать синтаксис инициализатора объекта, и это назначение было бы в блоке инициализации ...

Кто-нибудь знает более изящное решение?


person Grank    schedule 16.09.2008    source источник
comment
Проверьте соответствующую запись в блоге Эрика Липперта: Проблемы с выводом типов, часть первая   -  person CodesInChaos    schedule 21.04.2012
comment
Ошибка компилятора CS0173 привел меня сюда.   -  person DavidRR    schedule 23.05.2014
comment
C # 9 поддерживает целевую типизацию условных выражений, что снижает вероятность возникновения этой проблемы. . В примере, приведенном в этом вопросе, компилятор определит тип из свойства EmployeeNumber, если его тип - int?, но вы все равно получите ошибку при назначении более обобщенному типу, например object, или недавно объявленной переменной var.   -  person binki    schedule 02.12.2020


Ответы (6)


Проблема возникает из-за того, что условный оператор не смотрит, как значение используется (присваивается в данном случае) для определения типа выражения - только истинное / ложное значения. В этом случае у вас есть null и Int32, и тип не может быть определен (есть реальные причины, по которым он не может просто предполагать Nullable<Int32>).

Если вы действительно хотите использовать его таким образом, вы должны сами привести одно из значений к Nullable<Int32>, чтобы C # мог разрешить тип:

EmployeeNumber =
    string.IsNullOrEmpty(employeeNumberTextBox.Text)
    ? (int?)null
    : Convert.ToInt32(employeeNumberTextBox.Text),

or

EmployeeNumber =
    string.IsNullOrEmpty(employeeNumberTextBox.Text)
    ? null
    : (int?)Convert.ToInt32(employeeNumberTextBox.Text),
person Alex Lyman    schedule 16.09.2008
comment
Вы также можете написать new int?(). - person SLaks; 09.04.2010
comment
Вы говорите, что есть реальные причины, по которым он не может использовать Nullable ‹Int32›. Какие причины? - person BlueMonkMN; 09.08.2010
comment
@SLaks Или default(int?). - person IS4; 21.03.2015
comment
Настоящие причины? Я тоже не вижу настоящих причин. - person user1060500; 05.06.2015

Я думаю, что служебный метод может помочь сделать это чище.

public static class Convert
{
    public static T? To<T>(string value, Converter<string, T> converter) where T: struct
    {
        return string.IsNullOrEmpty(value) ? null : (T?)converter(value);
    }
}

тогда

EmployeeNumber = Convert.To<int>(employeeNumberTextBox.Text, Int32.Parse);
person NerdFury    schedule 16.09.2008
comment
Это действительно хорошая идея и пример того, как универсальные методы и методы расширения чрезвычайно хороши :) - person Grank; 16.09.2008

Хотя Алекс дает правильный и точный ответ на ваш вопрос, я предпочитаю использовать TryParse:

int value;
int? EmployeeNumber = int.TryParse(employeeNumberTextBox.Text, out value)
    ? (int?)value
    : null;

Это безопаснее и учитывает случаи неверного ввода, а также сценарий с пустой строкой. В противном случае, если пользователь вводит что-то вроде 1b, ему будет представлена ​​страница с ошибкой с необработанным исключением, вызванным в Convert.ToInt32(string).

person user13493    schedule 16.09.2008
comment
Я предпочитаю проверять ввод пользователя с помощью ASP.NET CompareValidator, который может быть установлен в качестве оператора DataTypeCheck и типа int. Это гарантирует, что ввод никогда не будет ничем иным, кроме целого числа, и делает это на стороне клиента, если это возможно. - person Grank; 17.09.2008

Вы можете преобразовать вывод Convert:

EmployeeNumber = string.IsNullOrEmpty(employeeNumberTextBox.Text)
   ? null
   : (int?)Convert.ToInt32(employeeNumberTextBox.Text)
person Abe Heidebrecht    schedule 16.09.2008

//Some operation to populate Posid.I am not interested in zero or null
int? Posid = SvcClient.GetHolidayCount(xDateFrom.Value.Date,xDateTo.Value.Date).Response;
var x1 = (Posid.HasValue && Posid.Value > 0) ? (int?)Posid.Value : null;

РЕДАКТИРОВАТЬ: краткое объяснение вышесказанного, я пытался получить значение Posid (если оно не равно нулю int и имеет значение больше 0) в varibale X1. Мне пришлось использовать (int?) на Posid.Value, чтобы условный оператор не выдавал никаких ошибок компиляции. Просто FYI GetHolidayCount - это WCF метод, который может дать null или любое число. надеюсь, это поможет

person Sandeep    schedule 24.04.2015
comment
Обычно рекомендуется включать описание того, что делает ваш код, вместо того, чтобы просто публиковать код. Это помогает человеку, задающему вопрос (и людям, которые нашли этот вопрос, столкнувшись с той же проблемой), понять, что делает ваше решение. - person Robert Rouhani; 24.04.2015

Начиная с C # 9.0, это, наконец, станет возможным:

Цель введена ?? и ?:

Иногда условно ?? и?: выражения не имеют очевидного общего типа между ветвями. Такие случаи сегодня терпят неудачу, но C # 9.0 разрешит их, если есть целевой тип, в который конвертируются обе ветви:

Person person = student ?? customer; // Shared base type
int? result = b ? 0 : null; // nullable value type

Это означает, что блок кода в вопросе также будет компилироваться без ошибок.

EmployeeNumber =
string.IsNullOrEmpty(employeeNumberTextBox.Text)
    ? null
    : Convert.ToInt32(employeeNumberTextBox.Text),
person Glorfindel    schedule 04.07.2020