Как ограничить параметр универсального типа, чтобы он принимал только типы значений, допускающие значение NULL, в C#?

Ограничение where T : struct позволяет ограничить область допустимых параметров типа только набором типов значений (по сравнению с надмножеством типов, включающим как значения, так и ссылочные типы), но также, по-видимому, полностью запрещает типы, допускающие значение NULL, хотя значение NULL не обязательно означает ссылочный тип в современных версиях C#.

Что, если я хочу принять типы значений с добавленной возможностью обнуления, такие как int?, DateTime? и т. д., и отклонить ссылочные типы с изначально обнуляемыми значениями, такие как строка, IList и т. д.? Можно ли таким образом определить ограничения? Как, если это так?

Мне на самом деле любопытно научиться реализовывать оба сценария: когда тип, используемый в качестве параметра, должен быть как значением, так и обнуляемым, и когда должен быть принят тип значения, допускающий значение NULL, а также тип значения, не допускающий значение NULL, и я считаю, что это связано достаточно близко, чтобы простить упоминание обоих, поэтому я был бы признателен за скромный комментарий о втором случае и выбрал бы ответ, включающий его как лучший (учитывая, что другой ответ не будет действительно лучше в других отношениях), если будет более одного ответа отправлено, и мне придется выбирать, но на самом деле мне сейчас нужен первый случай (всегда требовать тип, который одновременно допускает значение NULL и является типом значения), и я также считаю, что второй случай будет быть довольно прямолинейным, учитывая знание первого, не говоря уже о том, что настаивать на склеивании двух вопросов в один - нехорошо, поэтому я буду очень признателен и приму ответ, касающийся только первого случая.


person Ivan    schedule 26.03.2017    source источник
comment
Вы пытались использовать System.Nullable‹T› в качестве ограничения? msdn.microsoft.com/en-us/library/b3h38hb0.aspx   -  person linuxuser27    schedule 26.03.2017
comment
к сожалению, общие ограничения на С# ограничены, и никто не может дать ответ с несуществующей функцией, даже Эрик Липперт :)   -  person Selman Genç    schedule 26.03.2017
comment
@SelmanGenç Д'о! это верно. Я постоянно забываю об этом ограничении. Передо мной не было VS, поэтому я мог проверить :)   -  person linuxuser27    schedule 26.03.2017
comment
@ linuxuser27 Я на самом деле написал это в OP, но да, это было первое, что я попробовал и потерпел неудачу :)   -  person Selman Genç    schedule 26.03.2017


Ответы (2)


Вы не можете. Nullable<T> не являются допустимыми ограничениями для дженериков в C#.

Когда вы пытаетесь сделать что-то вроде class X<T,U> where T : Nullable<U>, вы получаете следующую ошибку:

«А?» не является допустимым ограничением. Тип, используемый в качестве ограничения, должен быть интерфейсом, незапечатанным классом или параметром типа.

Если вам нужно принять как T, так и Nullable<T> в качестве параметров метода, вы можете просто предоставить переопределения:

class X<T>  where T : struct
{
   public void R(T arg){ Console.WriteLine("Non nullable: {0}", arg);}
   public void R(Nullable<T> arg){Console.WriteLine("Nullable: {0}", arg);}
}

И тогда вы можете вызвать любую версию:

X<int> r = new X<int>();
r.R((int?)4);
r.R(4);

В вашем типе имеет дело только с Nullable значениями, вы можете просто ограничиться T:struct, но везде внутри вашего класса используйте Nullable<T> для параметров и полей.

Дополнительные обсуждения конкретных аспектов - ограничение универсального типа C# для всего, допускающего значение NULL и сопутствующие вопросы.

person Alexei Levenkov    schedule 26.03.2017

Это не совсем то, что вам нужно, но, может быть, вы могли бы использовать ограничение типа IConvertible? В качестве интерфейса он допускает значение NULL и реализуется типами Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char и String.

class MyClass<T> where T : IConvertible
{
    //Etc
}
person John Wu    schedule 26.03.2017
comment
Хотя я не чувствую, что могу принять это, поскольку это не совсем ответ на указанный вопрос, позвольте мне выразить искреннюю благодарность за интересный вклад и проголосовать. - person Ivan; 26.03.2017