Почему Visual Studio не предупреждает меня об исключении нулевой ссылки? - PullRequest
2 голосов
/ 09 мая 2020

Интересно, почему Visual Studio 2019 не жалуется на этот фрагмент кода C#:

dynamic deserializedBody = JsonConvert.DeserializeObject(requestBody);
deserializedBody?.date.ToString();

Поскольку deserializedBody?.date может быть null, это будет означать, что метод ToString будет применяться к чему-то null. Я думаю, что что-то вроде этого:

deserializedBody?.date?.ToString();

было бы правильной формой для использования, но Visual Studio не жалуется на первую версию. Конечно, мне что-то не хватает об истинной природе этого фрагмента кода.

1 Ответ

8 голосов
/ 09 мая 2020

Оператор нулевого безопасного разыменования останавливает вычисление всего выражения, как только оно достигает нуля. Поэтому, если deserializedBody имеет значение null, он не будет пытаться оценить date и не будет вызывать ToString() ни для чего, поэтому вы не получите исключения.

Полный пример:

using System;

class Test
{
    DateTime date = DateTime.UtcNow;

    static void Main()
    {
        Test t = null;
        // No exception thrown
        Console.WriteLine(t?.date.ToString());
    }    
}

Здесь выражение t?.date.ToString() эквивалентно:

t is null ? null : t.date.ToString()

(за исключением того, что t оценивается только один раз). Это не эквивалентно

(t is null ? null : t.date).ToString()

... именно этого, я подозреваю, вы ожидали.

Но нет, это не защищает от ситуации, когда deserializedBody не равно NULL, а deserializedBody.date равно NULL.

Если вы ожидали предупреждения из-за C# 8 ссылочных типов, допускающих значение NULL, я считаю, что dynamic c выражения никогда не проверяются на null. В качестве примера рассмотрим:

class Test
{
    public string? foo;
}

class Program
{
    static void Main(string[] args)
    {
        Test t = new Test();
        dynamic d = t;

        Console.WriteLine(t.foo.Length); // Warning 
        Console.WriteLine(d.foo.Length); // No warning
        Console.WriteLine(d.bar); // No warning for this either
    }
}

I подозреваю Причина этого в том, что компилятор в основном не имеет информации о том, что такое d.foo. В каждом случае, когда есть полезное предупреждение, будет еще один случай, когда предупреждение будет бесполезным. Когда вы находитесь на территории динамического набора c, вы уже приняли на себя определенную степень риска - было бы странно предупреждать, что разыменование d.foo рискованно, когда доступ к d.foo рискован для начала.

...