description | title | ms.date | f1_keywords | helpviewer_keywords | ms.assetid |
---|---|---|---|---|---|
Compiler Error CS0165 |
Compiler Error CS0165 |
07/20/2015 |
CS0165 |
CS0165 |
e7eb7c90-af16-4734-b747-214030696975 |
Compiler Error CS0165
Use of unassigned local variable ‘name’
The C# compiler doesn’t allow the use of uninitialized variables. If the compiler detects the use of a variable that might not have been initialized, it generates compiler error CS0165. For more information, see Fields. This error is generated when the compiler encounters a construct that might result in the use of an unassigned variable, even if your particular code does not. This avoids the necessity of overly complex rules for definite assignment.
For more information, see Why does a recursive lambda cause a definite assignment error?.
Example 1
The following sample generates CS0165:
// CS0165.cs using System; class MyClass { public int i; } class MyClass2 { public static void Main(string[] args) { // i and j are not initialized. int i, j; // You can provide a value for args[0] in the 'Command line arguments' // text box on the Debug tab of the project Properties window. if (args[0] == "test") { i = 0; } // If the following else clause is absent, i might not be // initialized. //else //{ // i = 1; //} // Because i might not have been initialized, the following // line causes CS0165. j = i; // To resolve the error, uncomment the else clause of the previous // if statement, or initialize i when you declare it. // The following example causes CS0165 because myInstance is // declared but not instantiated. MyClass myInstance; // The following line causes the error. myInstance.i = 0; // To resolve the error, replace the previous declaration with // the following line. //MyClass myInstance = new MyClass(); } }
Example 2
Compiler error CS0165 can occur in recursive delegate definitions. You can avoid the error by defining the delegate in two statements so that the variable is not used before it is initialized. The following example demonstrates the error and the resolution.
class Program { delegate void Del(); static void Main(string[] args) { // The following line causes CS0165 because variable d is used // as an argument before it has been initialized. Del d = delegate() { System.Console.WriteLine(d); }; //// To resolve the error, initialize d in a separate statement. //Del d = null; //// After d is initialized, you can use it as an argument. //d = delegate() { System.Console.WriteLine(d); }; //d(); } }
xref:System.Reflection.Assembly.Load(System.String)
It’s due to the compiler difference.
In this fiddle, https://dotnetfiddle.net/5GgGNS, you can see the error, which is omitted in the mono compiler.
I think the error is valid due to the fact that this line
if (myDict?.TryGetValue("hello", out var value) == true)
is not guaranteed to initialize the local variable value
.
If you would rewrite it to:
if (myDict?.TryGetValue("hello", out var value) == null)
it would try to access value
.
Now, the null
value, or true
in your case, could be a function’s return value, which would only be known at run time.
But, since all variables are basically always initialized, it’s just a compiler feature.
On the other hand, according to the C#5 specs:
A local variable introduced by a local-variable-declaration is not automatically initialized and thus has no default value. For the purpose of definite assignment checking, a local variable introduced by a local-variable-declaration is considered initially unassigned. A local-variable-declaration may include a local-variable-initializer, in which case the variable is considered definitely assigned only after the initializing expression (§5.3.3.4).
But your code is C# 6.
So my conclusion is that the compilers interpret it differently. The Microsoft compiler takes the ?.
operator into account. You should file it as a bug, or finding at least, maybe even at both parties.
Argumentation
Fun fact, if you use this code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
//Your code goes here
Dictionary<string,int> myDict = null;
if (myDict?.TryGetValue("hello", out var value) == null)
{
Console.WriteLine("Hello" + value.ToString());
}
}
}
[using https://www.jdoodle.com/compile-c-sharp-online , mono 5.10.1]
You’ll see the actual initialization to default(T)
at work. The output is Hello0
. Nevertheless, it’s remarkable because due to the ?
, and the fact that myDict
is null
, TryGetValue
shouldn’t be called and leaving value
«uninitialized».
The null-conditional operators are short-circuiting. That is, if one operation in a chain of conditional member or element access operations returns null, the rest of the chain doesn’t execute.
source
But…, since there are no uninitialized variables; if it compiles, the compiler will make sure it’s behavior is not undefined.
So, since value
is initialized, on run-time, the question remains if it’s a valid compiler error at build time. Regarding to the run-time intend of the code it is (and that’s why the error was there in the first place), but I think it remains a grey area.
Do note that according to this default(T)
is not override-able, which would actually lead to no condition where it fails.
By running this little test:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
//Your code goes here
Dictionary<string,int> myDict = null;
if (myDict?.Bar(out var test) == null)
{
Console.WriteLine("does hit");
}
}
}
static class Foo
{
public static object Bar(this Dictionary<string,int> input, out int test)
{
test = 3;
Console.WriteLine("does not hit");
return 1;
}
}
[using https://www.jdoodle.com/compile-c-sharp-online , mono 5.10.1]
The output becomes:
does hit
And you can verify the correct run-time behavior of the ?.
operator.
adessolv 2 / 1 / 1 Регистрация: 14.06.2020 Сообщений: 33 |
||||
1 |
||||
02.09.2020, 02:18. Показов 4859. Ответов 14 Метки нет (Все метки)
Добрый вечер! Есть метод, добавляющий пассажиров автобуса — ввод возраста и пола с клавиатуры, после чего из файла берется случайное имя и приписывается этому пассажиру. Работает, пока не проставлен if, который должен на основе newGender выбирать один из 3 файлов (мужские, женские и унисекс имена). Кликните здесь для просмотра всего текста
Что здесь не так? Вроде бы newName присваивается значение от random_item… Спасибо!
__________________
0 |
Programming Эксперт 94731 / 64177 / 26122 Регистрация: 12.04.2006 Сообщений: 116,782 |
02.09.2020, 02:18 |
14 |
6269 / 3897 / 1567 Регистрация: 09.05.2015 Сообщений: 9,188 |
|
02.09.2020, 02:48 |
2 |
Что здесь не так? Вроде бы newName присваивается значение от random_item… Какое будет значение у
0 |
adessolv 2 / 1 / 1 Регистрация: 14.06.2020 Сообщений: 33 |
||||
02.09.2020, 12:04 [ТС] |
3 |
|||
Какое будет значение у newName если код внутри if не выполнится? Действительно… Но перед этим же newGender принимает значение. Тогда по идее вот так должно работать Кликните здесь для просмотра всего текста
Однако периодически для любого пола используются имена из списка Queer. То есть — newGender почему-то не срабатывает. Что надо править?
0 |
16930 / 12507 / 3286 Регистрация: 17.09.2011 Сообщений: 20,744 |
|
02.09.2020, 12:26 |
4 |
Но перед этим же newGender принимает значение. Там бесконечный цикл, выход из которого происходит только тогда, когда этой переменной присвоено значение.
Тогда по идее вот так должно работать А зачем присваивать переменной одно и то же значение в каждой ветке?
используются имена из списка Queer. То есть — newGender Как прогрессивно, толерантно и рукопожатно!!!1
0 |
adessolv 2 / 1 / 1 Регистрация: 14.06.2020 Сообщений: 33 |
||||
02.09.2020, 13:06 [ТС] |
5 |
|||
Там бесконечный цикл Я не понимаю . Где именно он бесконечный?
А зачем присваивать переменной одно и то же значение в каждой ветке? переменной newGender или newName?
Как прогрессивно, толерантно и рукопожатно у меня шведский учебный курс — иначе здесь никак А вот сейчас ругается на newName в цикле for: Кликните здесь для просмотра всего текста
И я теперь вообще не понимаю, почему так
0 |
kolorotur 16930 / 12507 / 3286 Регистрация: 17.09.2011 Сообщений: 20,744 |
||||||||||||
02.09.2020, 13:30 |
6 |
|||||||||||
Где именно он бесконечный? Вот тут:
переменной newGender или newName? Вот здесь у вас во всех ветках происходит одно и то же за исключением файла, из которого считываются имена.
Зачем столько дублирования?
А вот сейчас ругается на newName в цикле for Все по той же причине: компилятор не может гарантировать, что до выполнения этой строчки переменной будет присвоено значение.
1 |
2 / 1 / 1 Регистрация: 14.06.2020 Сообщений: 33 |
|
02.09.2020, 13:42 [ТС] |
7 |
Вот тут: лучше сделать switch от userInput, где в зависимости от нажатой буквы будет выбирать правильный пол?
// Кстати, почему первая строчка файла всегда пропускается? надо 0 вместо 1? При тестировании он один раз выбрал какую-то пустую строку и не определил имя
0 |
16930 / 12507 / 3286 Регистрация: 17.09.2011 Сообщений: 20,744 |
|
02.09.2020, 14:34 |
8 |
лучше сделать switch от userInput, где в зависимости от нажатой буквы будет выбирать правильный пол? Как вариант, но что-то мне кажется, что с современными трендами вам клавиатуры не хватит для букв…
надо 0 вместо 1? Ну 0 — это первая строчка, да.
При тестировании он один раз выбрал какую-то пустую строку и не определил имя Это уже после выбора надо проверять.
0 |
2 / 1 / 1 Регистрация: 14.06.2020 Сообщений: 33 |
|
02.09.2020, 14:37 [ТС] |
9 |
с современными трендами вам клавиатуры не хватит для букв… хаха мне всего лишь 3 надо, так что, думаю, тут хватит.
0 — это первая строчка Понятно, либо в каждом файле делать 1 строку пустой Ближе к вечеру все проверю, но идея мне ясна. Спасибо!
0 |
adessolv 2 / 1 / 1 Регистрация: 14.06.2020 Сообщений: 33 |
||||||||
02.09.2020, 21:56 [ТС] |
10 |
|||||||
Все-таки ничего хорошего не получилось… Кликните здесь для просмотра всего текста
Попытки сделать вот так
не удались из-за switch. Но хотя бы теперь чуть короче код. Теперь у меня полный ступор, и не понимаю, зачем все это. Помогите, пожалуйста!?
0 |
kotelok 963 / 590 / 203 Регистрация: 08.08.2014 Сообщений: 1,861 |
||||
03.09.2020, 08:28 |
11 |
|||
Сообщение было отмечено adessolv как решение Решениеadessolv Кликните здесь для просмотра всего текста
Добавлено через 23 минуты For the purpose of definite assignment checking, a local variable introduced by a local_variable_declaration is considered initially unassigned. (c) https://docs.microsoft.com/en-… /variables (раздел Local Variables).
1 |
2 / 1 / 1 Регистрация: 14.06.2020 Сообщений: 33 |
|
03.09.2020, 13:00 [ТС] |
12 |
kotelok, спасибо! А не могли бы вы сказать, что именно означает Код while (string.IsNullOrEmpty(newGender)) и Код switch (Console.ReadKey(false).Key) ? Точнее, что здесь проверяется?
0 |
963 / 590 / 203 Регистрация: 08.08.2014 Сообщений: 1,861 |
|
03.09.2020, 13:09 |
13 |
1. Цикл будет выполняться до тех пор, пока в теле цикла переменной ‘newGender’ не будет присвоено какой-нибудь значение, кроме null или пустой строки.
0 |
2 / 1 / 1 Регистрация: 14.06.2020 Сообщений: 33 |
|
03.09.2020, 13:25 [ТС] |
14 |
2. Производится считывание одного нажатия клавиши, берётся её строковое представление. Если строковое представление одно из [X,Y,Q], то переменной ‘newGender’ присваивается одно из значений и на следующей проверке условия цикла цикл завершается. Спасибо! То есть, если нажата какая-то другая клавиша — цикл начинается заново и длится до тех пор, пока newGender не будет присвоено значение Female, Male или Queer?
0 |
963 / 590 / 203 Регистрация: 08.08.2014 Сообщений: 1,861 |
|
03.09.2020, 13:28 |
15 |
adessolv Попробуйте запустить код в отладке, пройтись по шагам и посмотреть значения переменных на каждом этапе. Станет намного понятнее, да и опыт работы с отладчиком будет весьма полезен в любых задачах.
0 |
IT_Exp Эксперт 87844 / 49110 / 22898 Регистрация: 17.06.2006 Сообщений: 92,604 |
03.09.2020, 13:28 |
Помогаю со студенческими работами здесь Использование локальной переменной «b1», которой не присвоено значение Использование локальной переменной «*», которой не присвоено значение Ошибка 1 Использование локальной переменной «stat», которой не присвоено значение Не работает метод класса: Использование локальной переменной «cir», которой не присвоено значение Не могу исправить ошибку «использование локальной переменной которой не присвоено значение» при создании Word using… Не понятная ошибка «Использование локальное переменной, которой не присвоено значение» Искать еще темы с ответами Или воспользуйтесь поиском по форуму: 15 |
- Remove From My Forums
-
Question
-
Compiler error CS0165 is «Use of unassigned local variable ‘var'»
The documentation for this one states «If the compiler detects the use of a variable that
might
not have been initialized, it generates CS0165″.
So even though the compiler isn’t smart enough to figure out definitively whether or not my variable is going to be initialised by the time it’s used, it sees fit to refuse to compile. I’m sorry, Microsoft, but this is definitely NOT good enough. I’m being forced to do a needless expensive initialisation of a DateTime object because of this asinine decision on the compiler writers’ part.
You can’t go applying absolute behaviours when your detection of conditions is itself probabilistic. There must be some flag by which to change this one from an error to a warning, right?
Answers
-
Actually, «dob = new DateTime()» does not create a DateTime on the heap. It just zeros the memory for the dob variable. That’s one of the differences between reference types and value types. I find the syntax a bit confusing myself, so I can understand why you’d think that it was getting allocated and boxed onto the heap.
Looking at your code, I see the problem. As you said, the dob is only initialized within a try/catch block, and then its used after the try/catch block. If an exception was thrown within that block, and then the length of MsgLabel.Text.Length turned out to be less than or equal to 0, then dob would get used without having been initialized. Now, you and I can look at the code and tell that it wont happen unless some other thread changes MsgLabel.Text’s value between the execution of the catch block and the if statement. But, the compiler doesn’t make those kinds of inferrences, it just looks at the structure of the algorithm and enumerates all execution paths. Also, even if the compiler could make those kinds of inferrences, there is no way the compiler could ensure that the MsgLabel.Text wont get changed by another thread.
The simplest fix would probably be just to initialize dob to «new DateTime().» Another answer might be to create a method that sets the MsgLabel’s text and makes it visible, and then call that within each of your catch blocks. Then, add a return statement to each of the catch blocks and remove the if statement before SPSecurity.RunWithElevatedPrivileges.
-
Marked as answer by
Thursday, August 28, 2008 4:33 PM
-
Marked as answer by
#c# #tryparse #unassigned-variable
#c# #попробуйте проанализировать #неназначенная переменная
Вопрос:
После поиска я, похоже, не могу понять, почему компилятор C # жалуется, что локальная переменная dteDest не назначена в строке
if (dteSrc == dteDest) {
Ошибка исчезнет, если я заменю строку
DateTime dteSrc, dteDest;
с
DateTime dteSrc, dteDest = DateTime.MinValue;
Насколько я вижу, код никогда не достигнет строки сравнения, если dteDest не инициализирован DateTime .Попробуйте проанализировать, для чего это параметр out.
Моя логика:
- Если currentDataObj равно null, то booHaveOrigDate равно false, а первый if завершается ошибкой
- Если currentDataObj не равен null, но не может быть преобразован в DateTime, тогда booHaveOrigDate равно false, а первый if завершается ошибкой
- Дата-время.TryParse вернет false, если он не может преобразовать в DateTime это вместе с amp;amp; означает, что dteDest никогда не будет использоваться.
Простой пример кода
void StrangeLogic(object srcData, object currentDataObj) {
DateTime dteSrc, dteDest;
bool booHaveNewDate = DateTime.TryParse(srcData.ToString(), out dteSrc);
bool booHaveOrigDate = (currentDataObj != null)
amp;amp; DateTime.TryParse(currentDataObj.ToString(), out dteDest);
if (booHaveNewDate amp;amp; booHaveOrigDate) {
if (dteSrc == dteDest) {
// Get a "use of unassignned local variable 'dteDest'
// unless dteDest = DateTime.MinValue beforehand
}
}
}
Также, если я изменю строку
bool booHaveNewDate = DateTime.TryParse(srcData.ToString(), out dteSrc);
к следующему
bool booHaveNewDate = (srcData != null) amp;amp; DateTime.TryParse(srcData.ToString(), out dteSrc);
затем компилятор жалуется, что srcDate также не назначен.
Может ли кто-нибудь указать мне правильное направление на то, чего мне не хватает — я не имею в виду проверку параметров и т. Д. Меня беспокоит, почему логика компилятора кажется обманутой использованием общей функции TryParse?
Дополнительная информация
Даже расширение логики по-прежнему выдает ту же ошибку (использование неназначенной локальной переменной)
bool booHaveOrigDate;
if (currentDataObj != null)
booHaveOrigDate = DateTime.TryParse(currentDataObj.ToString(), out dteDest);
else
booHaveOrigDate = false;
if (booHaveOrigDate) {
if (dteSrc == dteDest) {
Похоже, что это то, что компилятор делает с проверкой null (currentDataObj != null), что мешает ему правильно определить, что dteDest не будет доступен, если не назначен
Измените его на этот код и никаких проблем (кроме возможных.toString() для нулевого объекта
bool booHaveOrigDate = DateTime.TryParse(currentDataObj.ToString(), out dteDest);
if (booHaveOrigDate) {
if (dteSrc == dteDest) {
Ответ №1:
Ваша замена неверна, она должна быть:
DateTime dteSrc = DateTime.MinValue, dteDest = DateTime.MinValue;
Однако вы должны использовать возвращаемую переменную TryParse , которая является bool, чтобы увидеть, сработал ли tryparse вместо этого, если ваш booHaveNewDate:
DateTime dteSrc, dteDest;
if(DateTime.TryParse(srcData.ToString(), out dteSrc) amp;amp; DateTime.TryParse(currentDataObj.ToString(), out dteDest))
{
if (dteSrc == dteDest) {
// Do your stuff here
}
}
Теперь вам не нужно назначать даты в самом начале.
** Вы должны протестировать этот код перед использованием, он не является производственным кодом и может содержать ошибки
Комментарии:
1. Спасибо за отзыв, я проиллюстрировал то, что компилятор жалуется на переменную dteSrc только тогда, когда я меняю строку, чтобы включить «(srcData != null) amp;amp; » в строку «bool booHaveNewDate = DateTime. Попробуйте проанализировать (srcData. toString(), out dteSrc); «. В противном случае он не жалуется на dteSrc. Извините, объяснение не было ясным в нижней части исходного вопроса.
2. Компилятор не может следовать вашей логике. Он просто недостаточно умен, чтобы следовать всем путям кода. Вы можете обмануть его, назначив им сначала переписать ваш код, чтобы он понимал ваш код. Строка (srcData != null) делает невозможным для компилятора увидеть, что происходит.
3. Конечно, этот код просто завершится ошибкой, если currentDataObj имеет значение null — эта проверка является основной причиной проблемы. Пропущенный чек на самом деле мало что решает.
4. Изначально у меня был такой код, но мне нужно было использовать результат теста TryParse в коде, который не был показан в примере. Ошибка появилась только тогда, когда я изменил ее на код, который я опубликовал. Кажется, это добавление «(currentDataObj != null) amp;amp;» перед анализом TryParse, который мешает синтаксическому анализу компилятора
5. Насколько я понимаю, сначала потребуется нулевой тест, чтобы убедиться, что . Метод toString() не завершается ошибкой при вызове TryParse
Ответ №2:
Компилятор формально корректен, присвоение dteDest
(в качестве out
параметра) является условным. В глазах компилятора этого может и не произойти. Компилятор не «понимает» логику, которая следует из TryParse() .
Здесь аналогичная ситуация:
int f(int x)
{
int r;
if (x <= 5) r = 1;
if (x > 5) r = 2;
return r; // error: use of uninitialized var
}
С другой стороны, кажется немного более логичным инициализировать с помощью
DateTime dteSrc = default(DateTime), dteDest = default(DateTime);
хотя это то же значение (DateTime.MinValue).
Комментарии:
1. Но не будет ли возвращаемое значение TryParse обрабатывать случай, когда преобразование завершается неудачно (вместо того, чтобы просто использовать DateTime. Разбор)
2. Да, поскольку составитель не оценивает код, он не «понимает», что «если (x <= 5) r = 1; если (x> 5) r = 2; » совпадает с «если (x <= 5) r = 1; иначе r = 2;» или»r = x <= 5? 1 : 2;»
Ответ №3:
Я могу ошибаться, но я не думаю, что компилятор пытается так тщательно проанализировать ваш код при сообщении об этой ошибке. В настоящее время я пытаюсь найти какой-либо источник для подтверждения моей теории. В то же время, я предполагаю, что это дизайнерское решение, потому что, если человеку требуется больше пары секунд, чтобы увидеть, что переменная не будет использоваться перед инициализацией, вероятно, лучшим решением для кодирования будет просто инициализировать ее нулем для начала, чтобы избежать путаницы.
Редактировать:
Ну, я немного осмотрелся, и хотя я нашел пару примеров людей, говорящих по сути то же самое, что и я, я не могу найти никакой официальной документации, подтверждающей это. Вот ответы, которые я нашел, хотя:
«Компилятор имеет полное право не знать вашу логику».
http://www.pcreview.co.uk/forums/use-unassigned-local-variable-error-t3067479.html
«… когда есть структура потока управления, он не может оценить ситуацию, потому что он не выполняет код, поэтому он не знает, присваиваются ли значения».
http://bytes.com/topic/c-sharp/answers/917965-why-am-i-getting-unassigned-local-variable-errors
Комментарии:
1. Спасибо, там есть несколько отличных моментов. Однако, похоже, это связано с тем фактом, что dteDest может быть неназначен, потому что он используется только тогда, когда оператор «DateTime. Попробуйте проанализировать (currentDataObj. toString(), out dteDest)», который является выходным параметром is. Его вводят в заблуждение, добавляя нулевой тест как часть логики
Ответ №4:
dteDest
не будет иметь установленного значения, если currentDataObj == null
Это сработает, если вы измените свою строку на:
bool booHaveOrigDate = DateTime.TryParse(currentDataObj != null ? currentDataObj.ToString() : string.Empty, out dteDest);
Комментарии:
1. Но ты не можешь позвонить
ToString()
дальшеnull
.2. Логический результат нулевого теста и дата-время. TryParse используется в «if (booHaveNewDate amp;amp; booHaveOrigDate) {«, который должен маскировать, не назначен ли dteSrc или dteDest
3. Если currentDataObj не назначен, он будет использовать строку. Пусто для TryParse, который вернет false и установит dteDest в MinValue .
Ответ №5:
логика компилятора, похоже, обманута использованием обычной функции TryParse
На приведенный выше вопрос проще всего ответить тем фактом, что когда компилятор компилирует ваш код, он не смотрит на то, что этот метод делает внутри, он просто смотрит на подпись. Он знает, что может возвращать логическое значение, которое может быть true или false, и он знает, что он устанавливает значение dteDest
.
На самом деле это не ваша проблема. Проблема заключается в том, что следующая строка:
bool booHaveOrigDate = (currentDataObj != null)
amp;amp; DateTime.TryParse(currentDataObj.ToString(), out dteDest);
Использует amp;amp;
оператор, который не будет оценивать вторую часть, если первая часть имеет значение false. Это называется оценкой короткого замыкания и работает по теории, что если первая часть false, то не имеет значения, какова вторая часть — общий результат всегда будет false.
Таким образом, в этом случае dteDest никогда не устанавливается, и компилятор чувствует, что это проблема, даже если вы посмотрите на логику и скажете, что код никогда не будет выполняться, если он не установлен.
Простой факт заключается в том, что люди часто могут определить оптимизацию и особые случаи, которые находятся за пределами компилятора. Вы говорите так, как будто уже знаете, что можете решить эту проблему, просто проверив параметр в начале, а затем вернув, если он равен нулю.
Комментарии:
1. Я точно понимаю, что вы имеете в виду, и это очень логично. Я обновил исходное содержимое quesiton разделом «Дополнительная информация», в котором показано расширенное if / else, которое все еще обманывает компилятор.
2. Возможно, я слишком много колебался. Важным моментом является то, что существует путь кода, в котором dteDest не задан. Это не имеет значения ни о чем другом, просто компилятор при просмотре всех путей в коде видит тот, который может привести к использованию объекта до его назначения. Он вообще не рассматривает, какая логика использует какие пути. Я вижу, как вы считаете это глупым, я был бы склонен согласиться в таком простом случае. Однако именно так работает компилятор. Если вы хотите углубиться в детали проектирования компилятора, я бы предложил задать отдельный конкретный вопрос.
It’s due to the compiler difference.
In this fiddle, https://dotnetfiddle.net/5GgGNS, you can see the error, which is omitted in the mono compiler.
I think the error is valid due to the fact that this line
if (myDict?.TryGetValue("hello", out var value) == true)
is not guaranteed to initialize the local variable value
.
If you would rewrite it to:
if (myDict?.TryGetValue("hello", out var value) == null)
it would try to access value
.
Now, the null
value, or true
in your case, could be a function’s return value, which would only be known at run time.
But, since all variables are basically always initialized, it’s just a compiler feature.
On the other hand, according to the C#5 specs:
A local variable introduced by a local-variable-declaration is not automatically initialized and thus has no default value. For the purpose of definite assignment checking, a local variable introduced by a local-variable-declaration is considered initially unassigned. A local-variable-declaration may include a local-variable-initializer, in which case the variable is considered definitely assigned only after the initializing expression (§5.3.3.4).
But your code is C# 6.
So my conclusion is that the compilers interpret it differently. The Microsoft compiler takes the ?.
operator into account. You should file it as a bug, or finding at least, maybe even at both parties.
Argumentation
Fun fact, if you use this code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
//Your code goes here
Dictionary<string,int> myDict = null;
if (myDict?.TryGetValue("hello", out var value) == null)
{
Console.WriteLine("Hello" + value.ToString());
}
}
}
[using https://www.jdoodle.com/compile-c-sharp-online , mono 5.10.1]
You’ll see the actual initialization to default(T)
at work. The output is Hello0
. Nevertheless, it’s remarkable because due to the ?
, and the fact that myDict
is null
, TryGetValue
shouldn’t be called and leaving value
«uninitialized».
The null-conditional operators are short-circuiting. That is, if one operation in a chain of conditional member or element access operations returns null, the rest of the chain doesn’t execute.
source
But…, since there are no uninitialized variables; if it compiles, the compiler will make sure it’s behavior is not undefined.
So, since value
is initialized, on run-time, the question remains if it’s a valid compiler error at build time. Regarding to the run-time intend of the code it is (and that’s why the error was there in the first place), but I think it remains a grey area.
Do note that according to this default(T)
is not override-able, which would actually lead to no condition where it fails.
By running this little test:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
//Your code goes here
Dictionary<string,int> myDict = null;
if (myDict?.Bar(out var test) == null)
{
Console.WriteLine("does hit");
}
}
}
static class Foo
{
public static object Bar(this Dictionary<string,int> input, out int test)
{
test = 3;
Console.WriteLine("does not hit");
return 1;
}
}
[using https://www.jdoodle.com/compile-c-sharp-online , mono 5.10.1]
The output becomes:
does hit
And you can verify the correct run-time behavior of the ?.
operator.
The null conditional ?.
removes the guarantee that value
will be assigned to since TryGetValue
will only be called conditionally if myDict
is not null
.
You enforce the assignment of value
inside the if
statement afterwards with == true
since the left side will return null
if TryGetValue
is not called due to myDict
itself being null. The compiler, however, cannot make this inference leap in the general case so you have to help it out either by testing myDict
for null
beforehand (and skipping ?.
) or initializing value
beforehand.