Дженерики и Nullable (класс против структуры) - PullRequest
6 голосов
/ 10 октября 2019

РЕДАКТИРОВАТЬ: см. Мое обновление ниже о моей позиции на Null в C # 8.0, прежде чем дать мне варианты шаблонов и таких


Исходный вопрос

Я пытаюсь обновить базовые библиотеки до «пустого», т. Е. С помощью флага C # 8.0 <Nullable>enable</Nullable>.

При попытке работать с абстракциями ив конкретных дженериках я столкнулся с некоторыми проблемами. Рассмотрим следующий фрагмент кода, который принимает Action и преобразует его в Func<TResult>. Это pre-nullable-enable :

public static Func<TResult> ToFunc<TResult>(this Action action)
        => () => { action(); return default; }
;

, но post-nullable-enable Кажется, мне трудно, потому что я не могу сделать TResult nullable (или * 1024)*) поскольку остроумие потребует ограничения либо where TResult: class, либо where TResult: struct. Я не могу объединить эти два ограничения, чтобы компилятор знал, что TResult может быть классом или типом значения. Что на данный момент меня раздражает - так как нужно уметь выражать , не имеет значения, будет ли класс или структура, оно будет обнуляться (независимо от предшествующего дизайна .NET).

Итак, post-nullable-enable Кажется, у меня есть только одна опция - дублирование кода, примеры ниже:

public static Func<TResult?> ToFuncClass<TResult>(this Action action)
        where TResult : class
        => () => { action(); return null; }
;

public static Func<TResult?> ToFuncStruct<TResult>(this Action action)
        where TResult : struct
        => () => { action(); return null; }
;

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


ОБНОВЛЕНИЕ : На самом деле, я думаю, что 'Я предпочел бы придерживаться моих собственных реализаций с нулевой обработкой , чем использовать обнуляемую функцию C # 8.0Как "Void" объект или выделенный "Option / Maybe / None" -solution , кажется, лучше передает информацию. Единственное, что меня беспокоит, это то, что это не очень полезно в продвижении языка вперед , в обучении новым кодировщикам и представлении другого третьего лица/ не нативное растворение в универсальное провлем, что у всех нас есть работа с нулем . Поскольку собственные реализации обработки с нулем хороши и все, но подходят по-разному в каждой кодовой базе, должны поддерживаться сообществом, и у вас есть различные варианты. Так что было бы очень полезно и чрезвычайно полезно, если бы язык полностью его соблюдал и если стандарт повысился. На что я надеялся, это будет - ясно, что это не так, и я понимаю. Но я чувствую, что это упущенная возможность.

Thx Yves

1 Ответ

4 голосов
/ 10 октября 2019

Причина объяснена в Попробуйте ссылочные типы, допускающие Nullable в разделе The issue with T?. По сути, нет никакого способа обойти это.

Во-первых, что такое T, когда мы используем T?? Это обнуляемый или не обнуляемый?

Естественное определение Т? будет означать «любой обнуляемый тип». Однако это будет означать, что T будет означать «любой необнуляемый тип», и это не так! Сегодня можно заменить T типом значения Nullable (например, bool?).

Во-вторых, типы, используемые в каждом случае, различны - string? по-прежнему string, а int? - Nullable<int>. Сгенерированные конкретные методы в каждом случае совершенно разные. В одном случае вы получите

Func<string> ToFuncClass<string>(this Action action)

В другом случае вы получите

Func<Nullable<int>> ToFuncStruct<int>(this Action)

Далее, важно отметить, что ссылочный тип, допускающий обнуление, не одно и то жекак обнуляемый тип значения. Типы значений Nullable отображаются в конкретный тип класса в .NET. Так инт? на самом деле Nullable. Но для строки? Фактически это та же строка, но с сгенерированным компилятором атрибутом, аннотирующим ее. Это сделано для обратной совместимости. Другими словами, строка? является своего рода "поддельным типом", тогда как int? is not.

Пример статьи демонстрирует это:

Это различие между типами значений, допускающими обнуляемое значение, и ссылочными типами, допускающими обнуляемое значение, проявляется в таком шаблоне:

void M<T>(T? t) where T: notnull

Это будет означать, что параметр является обнуляемой версией T, а T ограничено, чтобы быть нулевым.

Если бы T была строкой, то действительная подпись M была бы:

M<string>([NullableAttribute] T t)

, но если бы T был int, то M был бы

M<int>(Nullable<int> t)

Эти две подписи принципиально различны, и эта разница не может быть согласована.

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