Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the compiler choose overload with IEnumerable over IEnumerable<T>?

Tags:

c#

.net

Consider the following two extension methods:

using System;
using System.Collections.Generic;
using System.Linq;

public static class Extensions
{
    public static bool Contains(this IEnumerable self, object obj)
    {
        foreach (object o in self)
        {
            if (Object.Equals(o, obj))
            {
                return true;
            }
        }
        return false;
    }

    public static bool ContainsEither<T>(this IEnumerable<T> self, T arg1, T arg2)
    {
        return self.Contains(arg1) || self.Contains(arg2);
    }
}

When I wrote my second method, I intended it to call the generic LINQ Enumerable.Contains<T> method (type arguments inferred from the usage). However, I found out that it is actually calling the first method (my custom Contains() extension method. When I comment out my Contains() method, the second method compiles fine, using the Enumerable.Contains<T>() method.

My question is, why does the compiler choose my Contains() method with non-generic IEnumerable argument over Enumerable.Contains<T>() with IEnumerable<T> argument? I would expect it to choose Enumerable.Contains<T>() because IEnumerable<T> is more derived than IEnumerable.

like image 442
Mr Anderson Avatar asked Jun 17 '16 20:06

Mr Anderson


1 Answers

My question is, why does the compiler choose my Contains() method with non-generic IEnumerable argument over Enumerable.Contains<T>() with IEnumerable<T> argument?

Because it's found in the same namespace that contains the method calling it. Types declared within that namespace effectively have precedence over types declared in imported namespaces.

From the C# 5 specification, section 7.6.5.2:

The search for C proceeds as follows:

  • Starting with the closest enclosing namespace declaration, continuing with each enclosing namespace declaration, and ending with the containing compilation unit, successive attempts are made to find a candidate set of extension methods:
    • If the given namespace or compilation unit directly contains non-generic type declarations Ci with eligible extension methods Mj, then the set of those extension methods is the candidate set.
    • If namespaces imported by using namespace directives in the given namespace or compilation unit directly contain non-generic type declarations Ci with eligible extension methods Mj, then the set of those extension methods is the candidate set.
  • If no candidate set is found in any enclosing namespace declaration or compilation unit, a compile-time error occurs.
like image 195
Jon Skeet Avatar answered Oct 19 '22 00:10

Jon Skeet