Why can't generic methods choose a specific method if the generic doesn't match?

Given the following class:

public static class MWE
{
    public static void Foo<T, TColl>(Expression<Func<IFoo, bool>> bar, TColl foos)
        where TColl : IEnumerable<T>
    {
    }

    public static void Foo<T, TColl>(Expression<Func<IFoo, T>> bar, TColl foos)
        where TColl : IEnumerable<T>
    {
    }

    static MWE()
    {
        Foo(bar => bar.GetThing(), new List<IThing>());
        Foo(bar => bar.GetBool(), new List<IThing>());
    }
}

public interface IFoo
{
    IThing GetThing();

    bool GetBool();
}

      

I am getting a compile error on the second call Foo

, complaining that there is no conversion from List<IThing>

to IEnumerable<bool>

. It seems to me that the compiler doesn't see the first overload even though it does match (and the second doesn't).

Why? Is there a way that these two methods can coexist and work?

+3


source to share


3 answers


The first overload could not be called because there is not enough information to output the general arguments. You use one of the common arguments, TColl

but you never use T

in a signature, so there is no way to do that.

If you provide generic arguments explicitly, then the first overload will be selected and work correctly.



If you just completely remove the second common argument and use IEnumerable<T>

instead in the signature TColl

, it will simplify those methods and allow the corresponding functions of your existing calls.

+2


source


Generic constraints ( where ...

) are not used for generic inference (inference T

and TCall

unless you specify them explicitly). You might think that the type T

in your first overload might inferred as IThing

, because you are passing List<IThing>

as TColl

, but because of the above, it is not.



Since generic types cannot be inferred, this method does not participate in overload resolution and the second method is used. It fails and you see a compiler error.

+2


source


Look at this first:

public static void Foo<T, TColl>(Expression<Func<IFoo, T>> bar, TColl foos)
    where TColl : IEnumerable<T>

      

How does the compiler figure out what is T

? It looks at the expression you pass ( bar

) and looks at what the return type is. From this we can conclude T

. bar

returns IThing

, so T

- IThing

and TColl

- IEnumerable<IThing>

.

Now let's look at this:

public static void Foo<T, TColl>(Expression<Func<IFoo, bool>> bar, TColl foos)
    where TColl : IEnumerable<T>

      

What T

? It is not a return type bar

that is hardcoded as bool

. So what is it? The compiler cannot tell, so the only way to use this function is to explicitly specify what the general parameters are:

Foo<IThing,List<IThing>>(bar => bar.GetBool(), new List<IThing>());

      

^ this will compile.

Unable to figure out what it is T

, it has to use the first function, output T

is bool

, and then it will complain because you are passing it IEnumerable<IThing>

in when it expectsIEnumerable<bool>

0


source







All Articles