#c# #nullable #optional-parameters
#c# #обнуляется #необязательно-параметры
Вопрос:
Я использую null
для обозначения того, что необязательный параметр A? a
функции должен принимать ненулевое значение по умолчанию, вычисленное в функции. После ввода функции я проверяю, является ли переданный аргумент a
null
, и если да, то присваиваю a
ненулевое значение. С этого момента можно с уверенностью предположить, что a
это ненулевое значение. Проблема: компилятор не знает об этом, и теперь мне приходится ссылаться a.Value
на остальную часть функции, а не на простую a
.
Есть ли способ сообщить компилятору, что a
с какого-то момента он на самом деле не равен нулю? Если нет, то каков наиболее четкий способ работы с такими необязательными параметрами?
Пример кода:
using System; namespace test { public struct A { public int x; }; class Program { static void f(A? a = null) { // Assign the default value. if (a == null) a = new A { x = 3 }; // Now 'a' is non-null for the rest of the function. // What I do now: Console.WriteLine(a.Value.x); // What I'd like to do: // * Mark 'a' as non-null somehow. // Now can refer to 'a' directly: // Console.WriteLine(a.x); } static void Main(string[] args) { f(); } } }
Комментарии:
1. Введите новую ненулевую переменную:
A notNull = a ?? new A { x = 3 };
и используйте ее в остальной части метода (предположительно с лучшим именем, чемnotNull
…)2. Это другой подход, но я бы хотел избежать этого.
3. К сожалению , это не связано с компилятором:
A?
означаетNullablelt;Agt;
иNullablelt;Tgt;
обладает свойствомValue
. Пока у вас есть переменная типаNullablelt;Tgt;
, вам все равно нужно получить доступ к ееValue
свойству, чтобы получить содержащееся в ней значение4. По крайней мере, в C# 10 компилятор уже понимает, что
a
это уже неnull
так, когда он доходит доConsole.WriteLine
инструкции. Если я закомментирую оператор if, я получу предупреждение, котороеa
может быть равно нулю, но этого предупреждения нет при наличии оператора if. но!a
по-прежнему вводится какNullablelt;Agt;
, что означает, что вы не сможете уйти,a.Value
еслиA
на самом деле не извлекете экземпляр в отдельную переменную.5. @LasseV.Karlsen Я совершил ту же ошибку — на самом деле, ОП хочет использовать
Console.WriteLine(a.x)
, что, конечно, не будет компилироваться.
Ответ №1:
Нет, нет — не для типа значения, допускающего значение null. Во время компиляции тип значения , допускающего значение null, остается Nullablelt;Tgt;
неизменным, даже если компилятор знает, что он будет ненулевым.
Это отличается от типов ссылок с возможностью обнуления, в которых компилятор отслеживает, считает ли он, что переменная может быть нулевой, чтобы предупредить о разыменовании. С типами ссылок, допускающими обнуление, нет «реальных» отдельных типов, допускающих обнуление и не допускающих обнуления-только один ссылочный тип с указанием того, может ли значение быть нулевым.
Подход, заключающийся в введении новой ненулевой переменной, как указано в комментариях к вопросу, на самом деле является единственным способом продвижения вперед.
Ответ №2:
Похоже, вы просто хотите разрешить вызов метода без указания параметра (и в этом случае использовать определенное значение по умолчанию).
Существует еще одно возможное решение для этого конкретного сценария:. Просто перегрузка f()
, вот так:
static void f() { f(new A { x = 3 }); } static void f(A a) { Console.WriteLine(a.x); }
Затем код может вызывать f()
с параметром или без него, но разница в том, что в случае, когда он вызывает f(A a)
с параметром, он не может быть равен нулю.
(В ответ на ваш комментарий ниже.)
Если вы хотите справиться с возможностью вызова с возможно нулевой структурой, вместо этого вы можете перегрузиться следующим образом:
static void f(A? a = default) { f(a ?? new A { x = 3 }); } static void f(A a) { Console.WriteLine(a.x); }
Это означает, что вашей реализации не нужно иметь дело со значением, возможно, равным нулю, но вы все равно можете опустить аргумент на сайте вызова или вызвать его с нулевым значением.
Примечание: Вы можете упростить static void f(A? a = default)
, чтобы:
static void f(A? a = default) =gt; f(a ?? new A { x = 3 });
если вы предпочитаете более короткий синтаксис.
Комментарии:
1. Это тоже то, что я иногда делаю. Однако есть случаи, когда я пересылаю возможно нулевой параметр (так что я не знаю, является ли он нулевым), и в этом случае это не работает; т. Е. Тот, у которого необязательный параметр, является более общим подходом.
2. @kaba Смотрите мою правку.
3. Это последнее редактирование-интересный подход.
4. Этот ответ, похоже, в значительной степени попал в точку в моей проблеме; т. Е. Иметь необязательные параметры со значениями по умолчанию, обрабатывать их как ненулевые (потому что они теперь есть) и без необходимости изобретать посторонние имена переменных. Отличная работа!
Ответ №3:
Нет, это невозможно сделать. A?
это короткий путь для Nullablelt;Agt;
. Nullablelt;Agt;
очевидно , что это другой тип A
, и для того, чтобы получить значение, вам нужно его использовать Nullablelt;Agt;.Value
.
Лучший вариант, который у вас есть, — это, как прокомментировал @MatthewWatson, ввести другую переменную:
A aValue = a ?? new A { x = 3 };
Что-то, что может помочь, — это правильно назвать используемый параметр и фактическое значение, чтобы помочь их различить:
static void f(A? optionalA = null) { // Use optionalA if it has a value, or the default value otherwise. var a = optionalA ?? new A { x = 3 };
Комментарии:
1. Назвать сложно:( Если вызывающий абонент использует именованные параметры , как в
f(optionalA : new A {x = 4})
, то необязательный префикс-это просто шум для вызывающего абонента. Поэтому , возможно, интерфейс должен сохранить ясное имяa
, а реализация вместо этого должна принять удар. Иногда я называю новую переменную в реализацииa_
, т. Е. подчеркиванием.
Ответ №4:
Для этого вы должны иметь возможность использовать свойства.
С помощью свойств вы можете назначить полям новые значения и убедиться, что эти свойства либо возвращают ожидаемое значение, либо значение по умолчанию.
Подобный этому:
int? a = null; public int a2 { get { return a.GetValueOrDefault(3); } }
Тогда вы можете использовать свойство a2
вместо a
Не совсем то же самое, что изменить поле с обнуляемого на ненулевое, но это может сделать то, что вы ожидали сделать.