73

As per the title, is it possible to declare type-negating constraints in c# 4 ?

The use-case of interest was to allow the following overloads to co-exist

    void doIt<T>(T what){} 
    
    void doIt<T>(IEnumerable<T> whats){}

At the moment there is ambiguity because T in the first method could be an IEnumerable<> (so I would like to specify that T should NOT be IEnumerable)

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Cel
  • 6,467
  • 8
  • 75
  • 110
  • 1
    It is strange to observe you have such a requirement. you can only code against the type T that you know that belongs to a family of class. How can you code in generics otherwise ? Either you don't need generics in this case or you need to revise your use-cases. – this. __curious_geek Jan 04 '12 at 13:46
  • @LukeH I just tested it, you are right, resolution worked, but it always chose the first one. I did get an ambiguity earlier, which prompted me to ask this question, but that might have stemmed from another reason, or I just cannot find the conditions again when that ambiguity arose. – Cel Jan 04 '12 at 14:25
  • 8
    I draw your attention to the fact that types with methods that take a T and a sequence of T usually have different names for the two methods. Add and AddRange, in List, for example,. There's a reason for that. Follow that pattern. – Eric Lippert Jan 04 '12 at 14:51
  • Even if `T` is an `IEnumerable` there is no ambiguity, because then `IEnumerable` in the second `doIt` would have to be an `IEnumerable>` – Olivier Jacot-Descombes Jan 04 '12 at 15:15
  • 12
    Why the close vote? The answer to the question may be "no", but that doesn't mean the question is without value. – phoog Jan 04 '12 at 19:39
  • I have a use case... I have a "DistinctBy" extension method that takes in a property value to use as a key. (e.g. people.DistinctBy(person => person.Age).I really don't want people to do something that results in a boolean (e.g. listOfNumbers.DistinctBy(x => x / 2 > 10)) because they would be using it wrong. I can check at runtime, but it'd sure be nice to be able to check at compile time that "TKey" isn't a boolean =/ – Brent Rittenhouse May 31 '18 at 06:48
  • @Mitch Wheat yeah, I want to have special handling for Tasks -- if I just use Action and I allow task callbacks, everything breaks. -- I have special Async methods that take Action>, but the non-async ones should be invalid. -- I would like this enforced at compile time. – BrainSlugs83 Jul 09 '22 at 01:36

7 Answers7

60

No - there's no such concept either in C# or in the CLR.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    Will this concept make it to C# and/or CLR in the future? – Rand Random Jul 16 '15 at 14:25
  • @RandRandom: I haven't heard of any plans for it. – Jon Skeet Jul 16 '15 at 14:25
  • 4
    please add the latest C# version corresponding for that answer... in case in future this has to change... – serge Nov 08 '18 at 23:23
  • 6
    @Serge: I'd far rather not do that. It would apply to almost every answer to language-based questions on the site, and it's infeasible to revisit every answer every time there's a new version of C#. – Jon Skeet Nov 09 '18 at 09:44
  • 3
    @JonSkeet: It being infeasible to revisit old answers is a strong reason you *should* specify the current version when you answer. If you say "as of Version X", your answer will technically remain true even if something changes in Version Y. More importantly, someone reading this a decade from now will notice the old version number and not take the answer as gospel. – Travis Reed Nov 06 '20 at 22:51
  • 4
    @TravisReed: I think that would add a lot of noise to a lot of answers, to be honest. Note that the question already specifies a version of C#, and people *should* look at the date of an answer for context IMO. – Jon Skeet Nov 07 '20 at 07:43
8

I found my self trying to implement the same case mentioned in the comments:

void doIt<T>(IEnumerable<T> what) { }
void doIt<T>(T whats) { }

I expected the following code to reference the first method:

doIt(new List<T>());

But it actually references the second one.

One solution is to cast the argument like this:

doIt(new List<T>().AsEnumerable<T>());

The cast could be hidden by another overload:

void doIt<T>(List<T> whats) {
    doIt(whats.AsEnumerable<T>());
}
ANeves
  • 6,219
  • 3
  • 39
  • 63
Ricardo Valente
  • 581
  • 1
  • 10
  • 14
5

As far as I know it is not possible to do that.

What you can do is some runtime checking:

public bool MyGenericMethod<T>()
{
    // if (T is IEnumerable) // don't do this

    if (typeof(T).GetInterface("IEnumerable") == null)
        return false;

    // ...

    return true;
}
Didier Ghys
  • 30,396
  • 9
  • 75
  • 81
4

one use for this would be an option type.

public class Option<A,B> 
where A : !B
where B : !A
{
    private readonly A a;
    private readonly B b;

    private Option(){}

    public Option(A a) 
    {
        this.a = a
    }

    public Option(B b)  
    {
        this.b = b
    }
} 

runtime checking would of course work but you wouldn't have the benefit of type checking at compile time.

David
  • 41
  • 1
0

No, but it would be possible to check with an "is" and then handle it appropriately...

Jay
  • 3,276
  • 1
  • 28
  • 38
-1

As far as I know a Not contraint is not possible. You CAN use base classes and/or Interfaces to constrain a Generic. Faced with a similar problem leading to runtime failures, I implemented an interface on the classes the Generic was to handle:

public interface IOperations
{

}

public static T GenericOperationsFactory<T>(ILogger loggerInstance, ref ExecutionContext executionContext) 
        where T: IOperations
{
    var operationsContext = Factories.OperationsContextFactory(loggerInstance, ref executionContext);

    var operations = typeof(T).GetConstructor(new[] { typeof(OperationsContext) }).Invoke(new object[] { operationsContext });

    return (T)operations;
}

public abstract class OperationsBase:IOperations
{
    protected OperationsContext Context { get; set; }

    public OperationsBase(OperationsContext context)
    {
        Context = context;
    }
...

public class ListsOperations : OperationsBase
{
    public ListsOperations(OperationsContext context) :
        base(context)
    {

    }

alternatively:

public static T GenericOperationsFactory<T>(ILogger loggerInstance, ref ExecutionContext executionContext) 
        where T: OperationsBase
{
    var operationsContext = Factories.OperationsContextFactory(loggerInstance, ref executionContext);

    var operations = typeof(T).GetConstructor(new[] { typeof(OperationsContext) }).Invoke(new object[] { operationsContext });

    return (T)operations;
}

public abstract class OperationsBase
{
    protected OperationsContext Context { get; set; }

    public OperationsBase(OperationsContext context)
    {
        Context = context;
    }
...

public class ListsOperations : OperationsBase
{
    public ListsOperations(OperationsContext context) :
        base(context)
    {

    }
jlo-gmail
  • 4,453
  • 3
  • 37
  • 64
-4

You use a constraint so you can ensure the type you use has some properties/methods/... you want to use.

A generic with a type-negating constraint doesn't make any sense, as there is no purpose to know the absence of some properties/methods you do not want to use.

ken2k
  • 48,145
  • 10
  • 116
  • 176
  • 6
    obviously you have not received the error: ##'' cannot implement both '>' and '>' because they may unify for some type parameter substitutions## .. there are certainly cases when you want to specify that the genericstype2 cannot be generictype4 – Brett Caswell Oct 03 '14 at 21:26
  • 1
    I was able to work around my scenario by using a series of abstract classes that implement an instance of the similar interface, and inherit the abstract class.. I suppose that is how Action and so on do it.. – Brett Caswell Oct 03 '14 at 21:39