Параметр Null-coalescing out выдает неожиданное предупреждение

#c# #out #null-coalescing

#c# #выход #null-coalescing

Вопрос:

Используя эту конструкцию:

 var dict = new Dictionary<int, string>();
var result = (dict?.TryGetValue(1, out var value) ?? false) ? value : "Default";
  

Я получаю сообщение об ошибке, CS0165 use of unassigned local variable 'value' которое не соответствует моим ожиданиям. Как value может быть неопределенным? Если словарь равен null, то будет возвращен внутренний оператор, false который заставит внешний оператор принять значение false, возвращая Default .

Чего я здесь не понимаю? Это просто компилятор не может полностью оценить инструкцию? Или я что-то перепутал?

Комментарии:

1. Специальные состояния «Определенно назначенные после истинного выражения» или «Определенно назначенные после ложного выражения» отслеживаются только для ограниченного числа операторов. Насколько я понимаю, ?. и ?? среди них нет. Вместо этого вы могли бы использовать (dict != null amp;amp; dict.TryGetValue(1, out var value)) ? value : "Default" .

2. Да, это то, что я сделал, я также создал метод расширения, чтобы упростить работу. Было бы неплохо иметь возможность использовать такие конструкции, например, в синтаксисе запроса Linq без дополнительных методов расширения.

Ответ №1:

Ваш анализ верен. Это не анализ, выполняемый компилятором, потому что компилятор выполняет анализ, который требуется спецификацией C #. Этот анализ заключается в следующем:

  • Если условие condition?consequence:alternative выражения является константой времени компиляции true , то альтернативная ветвь недоступна; если false , то последующая ветвь недоступна; в противном случае доступны обе ветви.

  • Условие в этом случае не является константой, поэтому и следствие, и альтернатива достижимы.

  • локальная переменная value определенно назначается только в том случае, dict если она не равна null, и, следовательно, value не назначается определенно при достижении результата.

  • Но следствие требует, чтобы value было определенно назначено

  • Так что это ошибка.

Компилятор не такой умный, как вы, но это точная реализация спецификации C #. (Обратите внимание, что я не описал здесь дополнительные специальные правила для этой ситуации, которые включают предикаты типа «определенно назначается после истинного выражения» и так далее. Подробности см. в спецификации C #.)

Кстати, компилятор C # 2.0 был слишком умен. Например, если бы у вас было условие типа 0 * x == 0 для некоторого int local x , это вывело бы «это условие всегда истинно, независимо от того, каково значение x » и пометило бы альтернативную ветвь как недоступную. Этот анализ был правильным в том смысле, что он соответствовал реальному миру, но он был неправильным в том смысле, что в спецификации C # ясно сказано, что вычет должен производиться только для констант времени компиляции, и столь же ясно сказано, что выражения, включающие переменные, не являются постоянными.

Помните, цель этой вещи — найти ошибки, и что более вероятно? Кто-то написал, 0 * x == 0 ? foo : bar намереваясь, что это имеет значение «всегда foo «, или что они случайно написали ошибку? Я исправил ошибку в компиляторе, и с тех пор он строго соответствует спецификации.

В вашем случае ошибки нет, но код слишком сложен для анализа компилятором, поэтому, вероятно, также слишком сложно ожидать, что люди будут анализировать. Посмотрим, сможете ли вы это упростить. Что я мог бы сделать, так это:

 public static V GetValueOrDefault<K, V>(
  this Dictionary<K, V> d,
  K key,
  V defaultValue)
{
    if (d != null amp;amp; d.TryGetValue(key, out var value))
      return value;
    return defaultValue;
}
…
var result = dict.GetValueOrDefault(1, "Default");
  

Целью должно быть сделать сайт вызова читаемым; Я думаю, что мой сайт вызова значительно более читабелен, чем ваш.

Комментарии:

1. Согласитесь, ваше решение намного более читабельно. Я не уверен, что хочу скрыть проверку null на сайте вызова, но в любом случае это не повредит проверке null в методе расширения. Однако вопрос, вы хотели сказать, что » Условие в этом случае не является константой» во втором пуле?

Ответ №2:

Это просто компилятор не может полностью оценить инструкцию?

Да, более или менее.

Компилятор не отслеживает неназначенные, он отслеживает противоположные «определенно назначенные». Он должен где-то остановиться, в этом случае ему нужно было бы включить знания о библиотечном методе TryGetValue() . Это не так.

Комментарии:

1. Но все параметры out должны быть назначены перед возвратом из метода, за исключением случаев, когда метод выдает исключение, верно? Таким образом, компилятор должен знать, что значение out, по крайней мере, имеет значение по умолчанию, которое я ожидал. Если в коде указано ?? true , это приведет к неназначенному значению.

2. Да, я думаю, что @PetSerAl ближе к тому, что операторы ?. и ?? не рассматриваются.