C # — избегайте нулевых указателей в справочнике LINQ

#c# #linq #dictionary

#c# #linq #словарь

Вопрос:

У меня есть такой объект:

 class Moose
{
   public int? DatabaseID;   // (null if not stored)
   public string Name;
}

List<moose> meese;
 

и я хочу сгенерировать словарь, который сопоставляет DatabaseID с именем для тех meese, которые имеют ненулевой DatabaseID.

Прямой маршрут не работает:

 Dictionary<int, string> mapIdToName = meese.Where(moose => moose.DatabaseID != null).ToDictionary(moose => moose.DatabaseID, moose => moose.Name);
 

Я получаю

 Cannot implicitly convert type 'System.Collections.Generic.Dictionary<int?, string>' to 'System.Collections.Generic.Dictionary<int, string>'
 

Если бы я создавал список, я мог бы сделать это:

 List<int> databaseIDs = meese.Select(moose => moose.DatabaseID).OfType<int>();
 

Но я не могу найти ничего подобного для ToDictionary.

Сейчас я просто делаю это вручную:

 Dictionary<int, string> mapIdToName = new Dictionary<int, string>();
foreach (var moose in meese)
{
   if (moose.DatabaseID != null)
      mapIdToName[moose.DatabaseID] = moose.Name;
}
 

Есть ли умный способ Linq сделать это?

Редактировать

У меня есть

 #nullable enable
 

итак, делая это

 Dictionary<int, string> mapIdToName = meese.Where(moose => moose.DatabaseID.HasValue).ToDictionary(moose => moose.DatabaseID.Value, moose => moose.Name);
 

не выдает ошибку, но выдает предупреждение «Тип значения с нулевым значением может быть null»

Ответ №1:

Вы можете сделать

 var map = meese.Where(moose => moose.DatabaseID.HasValue).ToDictionary(moose => moose.DatabaseID!.Value, moose => moose.Name);
 

Обратите внимание на after , or, где вы в основном сообщаете компилятору, что я точно знаю, что это не так уж и сложно. ! DatabaseID dammit operator null forgiving operator null

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

1. О, я ненавижу оператор dammit (это похоже на проигрыш), но это может быть случай, когда это необходимо

2. Или GetValueOrDefault

Ответ №2:

У вас почти получилось с первой попытки. При сопоставлении нулевого типа с его базовым типом вам нужно не забыть удалить значение. Это должно быть что-то вроде этого:

 Dictionary<int, string> mapIdToName = meese.Where(moose => moose.DatabaseID.HasValue).ToDictionary(moose => moose.DatabaseID.Value, moose => moose.Name);
 

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

1. Это становится ближе, сначала я не понимал, что включение NRT изменит ситуацию, поэтому я отредактировал свой исходный пост. Компилятор принимает это, и люди, читающие его, согласятся, что это правильно, но компилятор все еще нервничает (выдает мне предупреждение), что лось. DatabaseID.Value может быть нулевым

2. Я думаю, основная проблема заключается в том, что Where() возвращает тот же тип, который он задан, поэтому даже после Where(), который, как мы понимаем, отфильтровывает значения null, тип по-прежнему остается Dictionary<int?, string>

3. просто укажите ID.Value, например ` meese. Где(a=>a.DatabaseID.HasValue) . ToDictionary(a=>a.DatabaseID.Value, b=>b.Name )`

4. Да, это то, что предложил Гринджед — это лучше, потому что я не получаю ошибку во время компиляции, но я все равно получаю предупреждение (я обновил свой пост, чтобы включить эту информацию)