В C# к типу можно добавить вопросительный знак, чтобы указать, что он может содержать либо значение типа, либо возвращать null:
double? MySqrt(double a)
{
return (a > 0.0) ? Math.Sqrt(a) : null;
}
Console.WriteLine(MySqrt(4.5)); // 2.1213203435596424
Console.WriteLine(MySqrt(-4.5)); // a blank line
Теперь, если я хочу обусловить присвоение переменной результата этой функции, я могу использовать оператор ??
, например.
double b = MySqrt(-2.0) ?? 0.0;
Console.WriteLine(b); // 0
Но без переопределения MySqrt
, как я могу управлять более широким программным потоком (а не только присваиванием одной переменной) в зависимости от того, вернуло ли MySqrt
null
?
Например, следующее приводит к ошибке компилятора:
void DrawSquareWithArea(double area)
{
double sideLength = MySqrt(area);
if (sideLength != null)
{
// Draw a square on the screen
DrawSquare(sideLength);
}
else
{
// Display some visual feedback indicating that input was invalid
DisplayErrorFeedback();
}
}
// error CS0266: Cannot implicitly convert type 'double?' to 'double'. An explicit conversion exists (are you missing a cast?)
Обратите внимание, что функция DrawSquare()
требует аргумента double
, поэтому изменение sideLength
на double?
является еще одной ошибкой компилятора.
Это тоже ошибка компилятора, потому что для функции DrawSquare требуется двойник.
DrawSquare(sideLength.Value);
возможно?
Если вы рассматриваете double?
как коробку, внутри которой либо double
, либо ничего нет, вы можете извлечь double
из этой коробки, используя sideLength.Value
(но если там ничего не было, будет выдано исключение, хотя в этом случае вы сначала проверили, что там что-то есть).
Используя правильный тип (Nullable<double>
он же double?
). Доступ к ненулевому значению можно получить с помощью свойства Value
(которое здесь имеет тип double
):
void DrawSquareWithArea(double area)
{
double? sideLength = MySqrt(area);
if (sideLength != null) // or: if (sideLength.HasValue)
{
// Draw a square on the screen
DrawSquare(sideLength.Value);
}
else
{
// Display some visual feedback indicating that input was invalid
DisplayErrorFeedback();
}
}
Или попросите компилятор вывести тип:
var sideLength = MySqrt(area);
Да, вопрос был отредактирован, чтобы уточнить комментарий. Value
возвращает необработанное значение (т.е. double
)
@Max Я пошел дальше и удалил свой комментарий из-за правок и того, что вы следите за комментариями. Я чувствую, что это больше не добавляет ценности ответу. Я вернусь позже, чтобы удалить этот комментарий. Не стесняйтесь удалять любые комментарии, которые вы хотите.
Вы не можете явно преобразовать double?
в double
. Либо проверьте null
, либо используйте .Value
:
void DrawSquareWithArea(double area)
{
double? sideLength = MySqrt(area);
if (sideLength is not null)
{
// Draw a square on the screen
DrawSquare(sideLength.Value); // sideLength.Value is double
}
//...
}
или используйте сопоставление шаблонов с шаблоном объявления:
void DrawSquareWithArea(double area)
{
if (MySqrt(area) is {} len) // checks for null and assigns variable of double type
{
// Draw a square on the screen
DrawSquare(len);
}
//...
}
или :
void DrawSquareWithArea(double area)
{
if (MySqrt(area) is double len) // checks for null and assigns variable of double type
{
// Draw a square on the screen
DrawSquare(len);
}
//...
}
Читать далее:
Другие ответы в порядке. Однако я хотел бы добавить еще один подход, который может быть весьма удобным и (с моей точки зрения) может быть даже лучше, чем использование null в качестве возвращаемого типа. Если ваш метод вызывается с недопустимым значением, почему бы просто не выдать исключение? В вызывающем методе обработайте выброшенное исключение. Минимальный рабочий пример может выглядеть следующим образом.
class Program
{
static void Main(string[] args)
{
try{
MySqrt(-5);
} catch(Exception e) {
Console.WriteLine(e.Message);
}
}
static double MySqrt(double a)
{
if (a > 0.0) return Math.Sqrt(a);
throw new ArithmeticException("Can not calculate square root of negative value!");
}
}
Я не знаю о C#, но в других языках, которые я использую, блоги try/except обычно используются для отладки во время разработки и обычно не включаются в производственный код. Конвенция здесь другая?
Почти все более-менее сложные приложения могут столкнуться с исключением, о котором можно не думать во время разработки (как в вашем случае). Поэтому всегда полезно перехватывать исключения в какой-то момент (на максимально возможном уровне) в вашем коде и правильно их обрабатывать. Преимущество в том, что вы можете точно увидеть, где возникла проблема (если вы передадите объект исключения и зарегистрируете его). Но это просто предпочтения. Я просто хотел добавить другой подход :)
Ошибка очень ясна — замените
double sideLength
либо наvar sideLength
, либо наdouble? sideLength
.