Можно ли определить ограничение not Nullable‹T› в универсальном методе С#?

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

T DoSomething<T>() where T : struct
{
   //... 
} 
DoSomething<int?>(); //not ok
DoSomething<int>();  //ok

И, конечно же, Nullable<T> также не удовлетворяет ограничению ссылочного типа where class:

T DoSomething<T>() where T : class
{
   //...
} 
DoSomething<int?>(); //not ok
DoSomething<Foo>();  //ok

Возможно ли определить ограничение, которое должно быть ссылочным типом или типом значения, но не типом значения, допускающим значение Null?

Что-то вроде этого :

void DoSomething<T>() where T : class, struct //wont compile!
{    
   //...   
} 
DoSomething<int?>(); //not ok
DoSomething<int>();  //ok
DoSomething<Foo>();  //ok

person tigrou    schedule 11.02.2015    source источник
comment
Все, кроме Nullable<T>? Это сложно.   -  person Vitor Canova    schedule 11.02.2015
comment
почему ты хочешь сделать это?   -  person sloth    schedule 11.02.2015
comment
Насколько я знаю, это невозможно с общим ограничением, поэтому для этого нет проверки во время компиляции. Однако вы можете проверить фактический тип во время выполнения.   -  person dave    schedule 11.02.2015
comment
Вы можете сделать это с перегрузками и необязательными параметрами, но это довольно неприятно. См. codeblog.jonskeet.uk/2010/11/ 02/ Если вы можете предоставить нам больше информации о том, чего вы пытаетесь достичь, мы сможем помочь вам больше.   -  person Jon Skeet    schedule 11.02.2015
comment
@sloth: у меня есть метод Add<TValue>(Func<TViewModel, TValue> expression), и я хотел бы убедиться, что он используется только с типами, не допускающими значение NULL (например: Add(x => x.Id) OK Add(x => x.CreationDate.Value) OK Add(x => x.CreationDate) NOK ).   -  person tigrou    schedule 11.02.2015
comment
@tigrou - я думаю, мы это понимаем. Мы хотим знать, что вы на самом деле пытаетесь сделать, а не как вы пытаетесь это сделать.   -  person Enigmativity    schedule 11.02.2015
comment
@tigrou Но если вы используете ссылочный тип (класс), значение также может быть нулевым.   -  person sloth    schedule 11.02.2015


Ответы (2)


Нет, это невозможно на стороне декларации. Либо struct, либо class. Однако вы можете проверить typeof(T) во время выполнения, чтобы убедиться, что T соответствует Nullable<T2>.

Type type = typeof(T);
if(Nullable.GetUnderlyingType(type) == null)
    throw new Exception();
person rducom    schedule 11.02.2015

Как отмечено в комментарии, вы можете сделать это с параметрами перегрузки и (которые могут быть необязательными). Я некоторое время назад писал об этом в блоге, но в вашем случае вы хотели бы:

public class ClassConstraint<T> where T : class
{
}

public class SomeClass<TViewModel>
{
    public void Add<TValue>(Func<TViewModel, TValue> expression,
                            ClassConstraint<TValue> ignored = null)
        where TValue : class
    {
        AddImpl(expression);
    }

    public void Add<TValue>(Func<TViewModel, TValue> expression,
                            Nullable<TValue> ignored = null)
        where TValue : struct
    {
        AddImpl(expression);
    }

    // No constraints
    private void AddImpl<TValue>(Func<TViewModel, TValue> expression)
    {
        ...
    }
}

Это некрасиво, но это работает:

var z = new SomeClass<string>();
z.Add(x => x.Length);        // Valid (non-nullable value type)
z.Add(x => x);               // Valid (reference type)
z.Add(x => new DateTime?()); // Invalid (nullable value type)
person Jon Skeet    schedule 11.02.2015