Cel
Cel

Reputation: 6669

generic NOT constraint where T : !IEnumerable

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)

Upvotes: 80

Views: 28874

Answers (7)

Ricardo Valente
Ricardo Valente

Reputation: 591

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>());
}

Upvotes: 8

jlo-gmail
jlo-gmail

Reputation: 5048

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)
    {

    }

Upvotes: 0

David
David

Reputation: 39

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.

Upvotes: 3

Didier Ghys
Didier Ghys

Reputation: 30666

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;
}

Upvotes: 5

ken2k
ken2k

Reputation: 49013

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.

Upvotes: -4

Jay
Jay

Reputation: 3355

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

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1503050

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

Upvotes: 66

Related Questions