Получение ошибки о параметре типа Nullable, даже если параметр имеет ненулевое ограничение - PullRequest
2 голосов
/ 04 октября 2019

У меня общий интерфейс IDataAdapter<T>;Разработчики интерфейса должны иметь возможность считывать POCO с идентификаторами Guid из источника данных. IDataAdapter<T> имеет метод Read(Guid id), который я хочу вернуть T?, где ноль указывает, что в источнике данных не найдено совпадений. Однако даже при ограничении T : notnull на IDataAdapter<T> попытка определить этот метод дает ошибку CS8627: A nullable type parameter must be known to be a value type or non-nullable reference type. Consider adding a 'class', 'struct', or type constraint. Почему я все еще получаю эту ошибку, даже если T ограничено notnull?

Код (должен быть в среде C # 8 с <Nullable>enable</Nullable>):

interface IDataAdapter<T> where T : notnull
{
    T? Read (Guid id); // error CS8627
}

Ответы [ 3 ]

2 голосов
/ 04 октября 2019

Я думаю, что эта проблема очень похожа на то, что происходит в этом посте .

Обратите внимание, что T? where T : class и T? where T : struct представлены в CLR очень по-разному. Первый - это тип CLR T. В CLR нет отдельных типов, чтобы различать T и T?. T? в C # просто добавляет дополнительную проверку времени компиляции компилятором C #. С другой стороны, последний представлен типом CLR Nullable<T>.

Итак, давайте рассмотрим ваш метод:

T? Read (Guid id);

Как это должно быть представлено в CLR? Какой тип возврата? Компилятор не знает, является ли T ссылочным типом или типом значения, поэтому компилятор не может решить, должна ли подпись метода быть:

T Read (Guid id);

или:

Nullable<T> Read (Guid id);
0 голосов
/ 04 октября 2019

Та же ошибка возникает, если вы не используете ограничение notnull. Вам нужно указать, что это за тип с ограничением class или struct. Вам не нужно указывать notnull, так как структуры всегда имеют значение NULL, а также с включенными типами ссылок NULL, также как и классы.

Просто добавьте where T:class или where T:struct.

Типы ссылок

Если добавить ограничение class, например:

#nullable enable

interface IDataAdapter<T>       
    where T:class
{
    T? Read (Guid id); // error CS8627

    void Something(T input);
}

class StringAdapter:IDataAdapter<string>
{
    public string Read(Guid id)=>id.ToString();

    public void Something(string input){}
}

Следующий вызов вызовет предупреждение:

var adp=new StringAdapter();
string? x=null;
adp.Something(x);  //CS8604: Possible null reference argument ....

Типы значений

Использование struct для создания IntAdapter с другой стороны приводит к ошибке компиляции, если аргумент обнуляется:

interface IDataAdapter<T>       
    where T:struct
{
    T? Read (Guid id); // error CS8627

    void Something(T input);
}


class IntAdapter:IDataAdapter<int>
{
    public int? Read(Guid id)=>id.ToString().Length;

    public void Something(int input){}
}

void Main()
{

    var adp=new IntAdapter();
    int? x=null;
    adp.Something(x);  //CS1503: Cannot convert from int? to int
}

Это потому, чтосгенерированные методы, которые ожидают int? вместо int.

Объяснение

Причина в том, что компилятор должен генерировать очень разные коды в каждомкейс. Для класса это не должно делать ничего особенного. Для структуры необходимо сгенерировать Nullable .

Это объясняется в разделе The issue with T? в Попробуйте ссылочные типы Nullable :

Это различие между типами значений Nullable и ссылочными типами Nullable встречается в такой схеме:

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

Это будет означать, что параметр является обнуляемой версией T и Tвынужден быть ненулевым. Если бы T была строкой, то фактическая сигнатура M была бы M ([NullableAttribute] T t), но если бы T был int, то M был бы M (Nullable t). Эти две сигнатуры принципиально различны, и эта разница не может быть согласована.

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

0 голосов
/ 04 октября 2019

Если вы посмотрите на документацию Nullable Struct , вы увидите, что она должна быть struct:

public struct Nullable<T> where T : struct

Я полагаю, что вам нужно ограничить T, чтобыa struct:

interface IA<T> where T : struct
{
    T? Read(Guid id);
    // Or Nullable<T> Read(Guid id);
}


class A : IA<int>
{
    public int? Read(Guid id) { Console.WriteLine("A"); return 0; }
}

Кстати. Не могли бы вы привести пример того, с какими типами вы хотите использовать этот класс?

Почему бы просто не использовать where T: class и возвращать T (или даже вообще не иметь ограничений)?

interface IA<T> where T : class
{
    T Read(Guid id);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...