+1  A: 

I think it has something to do with the fact that char is a value type and string is a reference type. It looks like you're defining

TOther : T

and char does not derive from string.

n8wrl
Hence my bewilderment why it's picking DoStuff<Tother>(IEnumerable<Tother>) as the method I'm invoking. I'm not specifying DoStuff<char> not is char derived from string. It doesn't make sense in any way.
Colin Burnett
I wonder if string can be cast to IEnumerable<char> and the compiler is inferring Tother to be char?
n8wrl
I think that's exactly what it's doing but I'm not invoking DoStuff<char>, just DoStuff. I have never seen the generic assumed like that.
Colin Burnett
a string is IEnumerable<char>
Matthew Whited
+2  A: 

The compiler will try to match the parameter to IEnumerable<T>. The String type implements IEnumerable<char>, so it assumes that T is "char".

After that, the compiler checks the other condition "where OtherT : T", and that condition is not met. Hence the compiler error.

Philippe Leybaert
+2  A: 

My GUESS, and this is a guess because I don't really know, is that it first looks in the derived class to resolve the method call (because your object is that of the derived type). If, and only if it can't, it moves on to looking at the base classes methods to resolve it. In your case, since it CAN resolve it using the

DoStuff <Tother>(IEnumerable<Tother> collection)

overload, it tried to jam it into that. So it CAN resolve it as far as the parameter is concerned, but then it hits a snag on the constraints. At that point, it's already resolved your overload, so it doesn't look any further, but just throws up an error. Make sense?

BFree
Except it's implicitly assuming Tother = char regardless of the fact that I'm not specifying that. I have never seen the generic on a method just simply assumed because it can jam the argument into one.
Colin Burnett
Have you never used LINQ? Most of LINQ is built on the idea that the type parameter may be assumed if the type is provided on one of the method parameters.
Matthew Whited
You are specifying that Tother is char, by passing in an IEnumerable<char>, aka "string". You might not be familiar with the "method type inference" feature of C#, but it has been around since C# 2.0. See the "type inference" section of my blog if you want details on how the method type inference algorithms work; they are quite fascinating.
Eric Lippert
+3  A: 

Edit

Ok... I think I see your confusion now. You would have expected DoStuff(string) to have kept the parameter as a string and walked the BaseClass Method List first looking for a suitable signature, and failing that fallback to trying to cast the parameter to some other type.

But it happened the other way around... Instead Container.DoStuff(string) went, meh "theres a base class method there that fits the bill, but I'm going to convert to an IEnumerable and have a heart attack about what's available in the current class instead...

Hmmm... I'm sure Jon or Marc would be able to chime in at this point with the specific C# Spec paragraph covering this particular corner case

Original

Both Methods expect an IEnumerable Collection

You're passing an individual string.

The compiler is taking that string and going,

Ok, I have a string, Both methods expect an IEnumerable<T>, So I'll turn this string into an IEnumerable<char>... Done

Right, Check the first method... hmmm... this class is a Container<string> but I have an IEnumerable<char> so that's not right.

Check the second method, hmmm.... I have an IEnumerable<char> but char doesn't implement string so that's not right either.

COMPILER ERROR

So what#s the fix, well it completely depends what your trying to achieve... both of the following would be valid, essentially, your types usage is just incorrect in your incarnation.

        Container<char> c1 = new Container<char>();
        c1.DoStuff("Hello World");

        Container<string> c2 = new Container<string>();
        c2.DoStuff(new List<string>() { "Hello", "World" });
Eoin Campbell
So the compiler picks an overload by only the types of the parameters and not the constraints placed upon them (not as a whole of parameters+constraints)? That seems rather weak and half-baked. It *can* find a method that it *can* use but it's opting *not* to.
Colin Burnett
no it can't... neither of your methods satisfy the type your passing in. Container<string> can currently only execute a DoStuff(IEnumerable<char>) OR DoStuff(IEnumerable<SomethingThatExtendsString>) <-- which is nothing since string is a sealed class.
Eoin Campbell
It can't? What happened to BaseContainer.DoStuff(T)? Regarding your fix edit. I want a container of string and the string "Hello World" to be DoStuff'ed. If I can't fix the class then DoStuff(new string[] {"Hello World"}); is what I want, but that's a really crummy API to provide (I think).
Colin Burnett
Your first sentence of your first comment is correct. The compiler picks the overload based on the types of the parameters, and not the constraints. The constraints are not part of the _signature_. Your third sentence is also correct. Your second sentence is not correct; this is neither weak nor half-baked. These rules were carefully designed, extremely strong, and are fully baked.
Eric Lippert
+2  A: 

As Eric Lippert explained, the compiler chooses the DoStuff<Tother>(IEnumerable<Tother>) where Tother : T {} method because it chooses methods before checking constraints. Since string can do IEnumerable<>, the compiler matches it to that child class method. The compiler is working correctly as described in the C# specification.

The method resolution order you desire can be forced by implementing DoStuff as an extension method. Extension methods are checked after base class methods, so it will not try to match string against DoStuff's IEnumerable<Tother> until after it has tried to match it against DoStuff<T>.

The following code demonstrates the desired method resolution order, covariance, and inheritance. Please copy/paste it into a new project.

This biggest downside I can think of so far is that you can not use base in the overriding methods, but I think there are ways around that (ask if you are interested).

using System;
using System.Collections.Generic;

namespace MethodResolutionExploit
{
    public class BaseContainer<T> : IEnumerable<T>
    {
        public void DoStuff(T item) { Console.WriteLine("\tbase"); }
        public IEnumerator<T> GetEnumerator() { return null; }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return null; }
    }        
    public class Container<T> : BaseContainer<T> { }
    public class ContainerChild<T> : Container<T> { }
    public class ContainerChildWithOverride<T> : Container<T> { }
    public static class ContainerExtension
    {
        public static void DoStuff<T, Tother>(this Container<T> container, IEnumerable<Tother> collection) where Tother : T
        {
            Console.WriteLine("\tContainer.DoStuff<Tother>()");
        }
        public static void DoStuff<T, Tother>(this ContainerChildWithOverride<T> container, IEnumerable<Tother> collection) where Tother : T
        {
            Console.WriteLine("\tContainerChildWithOverride.DoStuff<Tother>()");
        }
    }

    class someBase { }
    class someChild : someBase { }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("BaseContainer:");
            var baseContainer = new BaseContainer<string>();
            baseContainer.DoStuff("");

            Console.WriteLine("Container:");
            var container = new Container<string>();
            container.DoStuff("");
            container.DoStuff(new List<string>());

            Console.WriteLine("ContainerChild:");
            var child = new ContainerChild<string>();
            child.DoStuff("");
            child.DoStuff(new List<string>());

            Console.WriteLine("ContainerChildWithOverride:");
            var childWithOverride = new ContainerChildWithOverride<string>();
            childWithOverride.DoStuff("");
            childWithOverride.DoStuff(new List<string>());

            //note covariance
            Console.WriteLine("Covariance Example:");
            var covariantExample = new Container<someBase>();
            var covariantParameter = new Container<someChild>();
            covariantExample.DoStuff(covariantParameter);

            // this won't work though :(
            // var covariantExample = new Container<Container<someBase>>();
            // var covariantParameter = new Container<Container<someChild>>();
            // covariantExample.DoStuff(covariantParameter);

            Console.ReadKey();
        }
    }
}

Here is the output:

BaseContainer:
        base
Container:
        base
        Container.DoStuff<Tother>()
ContainerChild:
        base
        Container.DoStuff<Tother>()
ContainerChildWithOverride:
        base
        ContainerChildWithOverride.DoStuff<Tother>()
Covariance Example:
        Container.DoStuff<Tother>()

Can you see any problems with this work around?

dss539
Hmm, interesting idea. I didn't put those overloads on BaseContainer because I'm trying to keep that class very basic. I could easily put DoStuff<Tother> on BaseContainer but that kind defeats my goal of keeping BaseContainer basic. Extension means I technically keep BaseContainer the way I want it but... :)
Colin Burnett
Colin, it *doesn't* put the overloads on the BaseContainer. i.e. it doesn't junk up BaseContainer's intellisense. Try it please. If I misunderstood, please clarify.
dss539
My apologies, I misread the method prototype. Using an extension on a generic class does, however, mean that I have to invoke it by c.DoStuff<T, Tother> instead of just c.DoStuff<Tother>, right?
Colin Burnett
I think you missed another piece.... Console.WriteLine(c.DoStuff(new someChild())); Console.WriteLine(c.DoStuff(test)); So no, you invoke it as you please. No need to specify type parameters. I strongly urge you to copy paste this code into a new console app just to test it. I think you might like it.
dss539
A: 

I'm not really clear on what you're trying to accomplish, what's stopping you from just using two methods, DoStuff(T item) and DoStuff(IEnumerable<T> collection)?

The problem is when T itself is an IEnumerable<Anything> since the compiler latches onto DoStuff<Tother>(IEnumerable<Tother>), not DoStuff(T).
Colin Burnett