Подтвердить что ты не робот

Почему С# требует скобок при использовании nullables в выражении?

Я новичок в С# и, изучая особенности языка, наткнулся на что-то странное:

struct Foo
{
    public Foo Identity() { return this; }

    public static void Bar(Foo? foo)
    {
        Foo foo1 = foo?.Identity().Value; // Does not compile
        Foo foo2 = (foo?.Identity()).Value; // Compiles
    }
}

Может ли кто-нибудь объяснить мне, зачем нужна скобка?

4b9b3361

Ответ 1

Может ли кто-нибудь объяснить мне, зачем нужна скобка?

Потому что Identity() возвращает Foo (не a Foo?) и, следовательно, не имеет свойства Value. Если Foo имеет значение NULL, нуль будет распространяться по вызову Identity.

Когда вы помещаете круглые скобки вокруг него, результаты выражения представляют собой Nullable<Foo>, который имеет свойство Value.

Также обратите внимание, что если Foo равно null, вы будете вызывать Value на Nullable<Foo>, который не имеет значения, и получит исключение во время выполнения. Некоторые статические анализаторы признают, что у вас есть возможное исключение для нулевой ссылки, ожидающее появления и предупреждающее вас.

Если вы разложите их на эквиваленты без нулевого распространения, будет более понятно:

Foo foo1;
if(foo != null)
{
    foo1 = foo.Identity().Value;  // not possible - Foo has no Value property.
}
else
{
    foo1 = null;  // also not possible 
}

Foo foo2;
Foo? temp;
if(foo != null)
{
    temp = foo.Identity();
}
else
{
   temp = null;  // actually a Nullable<Foo> with no value
}
foo2 = temp.Value;  // legal, but will throw an exception at run-time if foo is null

Если Identity() возвращает Foo, почему Foo foo3 = foo?.Identity(); не компилируется?

Эквивалент этого будет:

Foo foo3
if(foo != null)
{
    foo3 = foo.Identity();
}
else
{
    foo3 = null;  // not possible 
}

Ответ 2

Я думаю, что это было хорошее решение команды С#, чтобы сделать это таким образом. Рассмотрим приведенный ниже сценарий:

Если структура была:

struct Foo
{
    public int ID { set; get; }

    public Foo Identity() { return this; }

    public static void Bar(Foo? foo)
    {
        int? foo1 = foo?.Identity().ID; // compile
        Foo foo2 = (foo?.Identity()).Value; // Compiles
    }
}

Если вам не нужна скобка для доступа к результату Nullable, вы не сможете получить доступ к свойству ID. Поскольку ниже не будет компилироваться:

int? foo2 = (foo?.Identity()).GetValueOrDefault()?.ID

Когда вы пишете foo?.Identity()., что после . имеет тип Foo, возвращаемый Identity(). Однако в (foo?.Identity()). то, что после . есть Foo?, которое является фактическим результатом всего оператора foo?.Identity().