Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extension methods and compile-time checking

Maybe a little tricky, but I wonder why. In System.Linq.Enumerable.cs of System.Core.dll we have:

public static int Count<TSource>(this IEnumerable<TSource> source);

In my code I'm doing something evil:

namespace Test
{
   public static class Extensions
   {
     public static int Count<TSource>(this IEnumerable<TSource> source)
     {
        return -1; //evil code
     }
   }

   //commented temporarily
   //public static class CommentedExtensions
   //{
   //  public static int Count<TSource>(this IEnumerable<TSource> source)
   //  {
   //     return -2; //another evil code
   //  }
   //}

   public static void Main(string[] args)
   {
     Console.WriteLine(Enumerable.Range(0,10).Count());   // -1, evil code works
     Console.Read();
   }
}

If I uncomment CommentedExtensions, I'll get a compile error saying "this call is ambiguous blabla" as expected. But why I didn't get this error at the first time? It's also ambiguous!

EDIT After another test, I found that I won't get compile errors if the extension methods are in different namespaces, even they are completely the same. Why it's allowed? It brings ambiguous call of methods in c#.

EDIT2 I know in fact the two Count are different in IL. In fact it's calling

Enumerable.Count(Enumerable.Range(0,10))

and my evil extension method is calling:

MyExtension.Count(Enumerable.Range(0,10))

so they are different. But still I think it's a bad smell. Do we have "real" extension methods? which can prevent the evil behavior?

like image 554
Cheng Chen Avatar asked Oct 13 '10 06:10

Cheng Chen


1 Answers

Section 7.6.5.2 of the C# language specification describes how the compiler determines which extension methods are in scope, and which extension methods take precedence over others :

The search for C [(a candidate extension method)] 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.

Meaning that if you have extension methods in the same namespace than the code from which you call them, these extension methods are selected. Extension methods in an enclosing namespace will be choosed over other namespaces that have been imported.

like image 136
Julien Hoarau Avatar answered Sep 16 '22 15:09

Julien Hoarau