Есть ли удобный способ отфильтровать последовательность обнуляемых ссылок C # 8.0, сохраняя только ненулевые? - PullRequest
6 голосов
/ 14 октября 2019

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

IEnumerable<string?> items = new [] { "test", null, "this" };
var nonNullItems = items.Where(item => item != null); //inferred as IEnumerable<string?>
var lengths = nonNullItems.Select(item => item.Length); //nullability warning here
Console.WriteLine(lengths.Max());

Как мне написать этот код удобным способом, таким образом:

  • Нет предупреждения об обнуляемости, потому что тип nonNullItems выводится как IEnumerable<string>.
  • Мне не нужно добавлять непроверенные утверждения, не допускающие обнуления, такие как item! (потому что я хочу воспользоваться проверкой работоспособности компиляторов, а не полагаться на то, что ябезошибочный кодер)
  • Я не добавляю проверенные во время выполнения утверждения об отсутствии обнуления (потому что это бессмысленные накладные расходы как по размеру кода, так и во время выполнения, а также в случае ошибки человека, которая завершается с ошибкой позже идеальной).
  • Решение или шаблон кодирования могут применяться в более общем случае к другим последовательностям элементов типа ссылочной единицы.

Мне известно об этом решении, которое использует типизацию, чувствительную к потоку, вкомпилятор C # 8.0, но он ... не такой красивый, в основном потому, что он такой длинный и шумный:

var notNullItems = items.SelectMany(item => 
    item != null ? new[] { item } : Array.Empty<string>())
);

Есть ли лучшая альтернатива?

Ответы [ 3 ]

5 голосов
/ 14 октября 2019

К сожалению, вам придется сообщить компилятору, что вы знаете больше о ситуации, чем она.

Одной из причин может быть то, что метод Where не был аннотирован вспособ, который позволяет компилятору понять гарантию необнуляемости, и при этом фактически невозможно аннотировать это. Может быть случай добавления дополнительной эвристики к компилятору для понимания некоторых базовых случаев, таких как этот, но в настоящее время у нас его нет.

Таким образом, одним из вариантов будет использование оператора прощения null, в разговорной речи известный как «оператор черт возьми». Вы сами к этому прикасаетесь, однако вместо того, чтобы разбрызгивать восклицательные знаки по всему коду, где вы используете коллекцию, вы можете вместо этого сделать дополнительный шаг при создании коллекции, которая, по крайней мере для меня, делает ееболее приемлемый:

var nonNullItems = items.Where(item => item != null).Select(s => s!);

Это пометит nonNullItems как IEnumerable<string> вместо IEnumerable<string?> и, таким образом, будет корректно обрабатываться в остальной части вашего кода.

1 голос
/ 14 октября 2019

Думаю, вам придется помочь компилятору тем или иным способом. Вызов .Where() никогда не является безопасным для возврата не нулевого значения. Возможно, Microsoft могла бы добавить какую-то логику для определения базовых сценариев, подобных вашему, но AFAIK, сейчас ситуация не такая.

Однако вы можете написать простой метод расширения, подобный этому:

public static class Extension
{
    public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> o) where T:class
    {
        return o.Where(x => x != null)!;
    }
}
0 голосов
/ 22 октября 2019

Я не знаю, соответствует ли этот ответ критериям вашего третьего пункта, но ваш фильтр .Where() тоже не подходит, поэтому ...

Заменить

var nonNullItems = items.Where(item => item != null)

с

var nonNullItems = items.OfType<string>()

Это приведет к выводу типа IEnumerable<string> для nonNullItems, и этот метод может быть применен к любому обнуляемому ссылочному типу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...