0

I have created the following class:

class GenericTest
{
    public T Do<T>( T test ) where T : class
    {
        return test;
    }

    public IEnumerable<T> Do<T>( List<T> test ) where T : class
    {
        return test;
    }

    public IEnumerable<T> Do<T>( IEnumerable<T> test ) where T : class
    {
        return test;
    }

}

This has three overloads of the Do() function. I'm trying to understand how the method parameter matching works in C# for generics, especially around interface parameters. So, I have the following test program:

static void Main( string[] args )
{
    GenericTest testing = new GenericTest();

    string s = "TEST";

    List<string> list = new List<string> {s};

    Stack<string> stack = new Stack<string>();
    stack.Push( s );

    testing.Do( s );  //calls public T Do<T>( T test ) 
    testing.Do( list ); //calls IEnumerable<T> Do<T>( List<T> test )
    testing.Do( stack ); //calls public T Do<T>( T test ) where T : class

}

The first call to Do() works as I expected, then the concrete class List parameter matches nicely against the List parameter method, but when I pass an IEnumerable, the compiler doesn't use the IEnumerable parameter method, instead it chooses the generic T method. Is this expected behaviour? Can I not overload with just an interface parameter in a generic?

9
  • 1
    possible duplicate of C# overloading with generics: bug or feature? Commented Jun 10, 2014 at 9:28
  • 1
    Eric lippert has a great post explaining your problem: blogs.msdn.com/b/ericlippert/archive/2009/12/10/… Commented Jun 10, 2014 at 9:28
  • It is a close duplicate but this question is a lot clearer. Commented Jun 10, 2014 at 9:38
  • @HenkHolterman So close that question as a duplicate of this one. Its accepted answer is a link-only answer so it's no use anyway Commented Jun 10, 2014 at 10:38
  • But then this one would get the same link-only answer, at best. Commented Jun 10, 2014 at 12:34

2 Answers 2

1

I'm not sure if first call works as expected since string is IEnumerable<char> ... Which method should be executed in such case? It depends on your particular requirement.

Behaviour that you are describing may be flexibly implemented following CoR pattern where you define matching logic and chain elements order according your particular needs.

Below is just an illustration that shows the idea(I'm sure it can be refactored):

public abstract class ChainElem
{
    public abstract bool IsMatching(object o);
    public abstract void Do(object o);
}

public class ChainElemIList : ChainElem{
    public override bool IsMatching(object o) {
        //Matches IList implementations only.
        if( o is IList )
            return true; 
        else
            return false;
    }

    public override void Do(object o) {
        //Do something with the IList
        Console.WriteLine("processing IList...");
    }
}

public class ChainElemIEnumerable : ChainElem{
    public override bool IsMatching(object o) {
        //Matches all IEnumerable implementations(but not string).
        //This is something that you won't achieve with generics.
        if( o is IEnumerable && !(o is string) )
            return true; 
        else
            return false;
    }

    public override void Do(object o) {
        //Do something with the IEnumerable(but not string)
        Console.WriteLine("processing IEnumerable(but not string)...");
    }
}

public class ChainElemString : ChainElem{
    public override bool IsMatching(object o) {
        //Matches strings only.
        if( o is string )
            return true; 
        else
            return false;
    }

    public override void Do(object o) {
        //Do something with the string
        Console.WriteLine("processing string...");
    }
}

public class ChainElemObject : ChainElem{
    public override bool IsMatching(object o) {
        //Matches everything else.  
        return true; 
    }

    public override void Do(object o) {
        //Do something with the object
        Console.WriteLine("processing object...");
    }
}

void Main()
{
    string s = "TEST";
    List<string> list = new List<string> {s};
    Stack<string> stack = new Stack<string>();
    stack.Push( s );
    object o = new object();

    //construct the chain - order is important and depends on your requirements
    var chain = new List<ChainElem> {
        new ChainElemIList(),
        new ChainElemIEnumerable(),
        new ChainElemString(),
        new ChainElemObject()
    };

    //processing
    chain.First(c => c.IsMatching(list)).Do(list);
    chain.First(c => c.IsMatching(stack)).Do(stack);
    chain.First(c => c.IsMatching(s)).Do(s);
    chain.First(c => c.IsMatching(o)).Do(o);

    //OUTPUT:
    //  processing IList...
    //  processing IEnumerable(but not string)...
    //  processing string...
    //  processing object...

}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the advice. The IEnumerable call never gets matched at all. I did think about using reflection to establish whether a parameter supported IEnumerable, but really I wanted the compiler to pick the correct method. I think the IEnumerable call may never get matched, but I feel the compiler should be spitting out a warning or error based on two matching method types, rather than picking one.
0

So, it seems that you can't overload with a interface parameter if you have generic method that matches the signature.

Either use a different name for your methods, or a concrete class in the parameter list.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.