LINQ Farm: Extension Methods and Scoping

There are a few scoping rules that you must keep in mind when using extensions methods. Problems with scoping and extensions methods are rare, but when you encounter them they are quite vexing.

An instance method will always be called before an extension method. The runtime looks first for an instance method, if it finds an instance method with the right name and signature, it executes it and never looks for your extension method. The following code illustrates this problem:

 using System;

namespace ConsoleApplication1
{
    public class MyClass
    {
        public void DoThis()
        {
            Console.WriteLine("MyClass.DoThis");
        }
    }


    public static class MyExtensions01
    {
        // Can never be called as if it were an instance method of MyClass.
        public static void DoThis(this MyClass myClass)
        {
            Console.WriteLine("MyExtensions01.DoThis");
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            MyClass myClass = new MyClass();
            myClass.DoThis();                // Calls MyClass.DoThis
            MyExtensions01.DoThis(myClass);  // Calls MyExtensions01.DoThis
        }
    }
}

MyExtensions01.DoThis is a valid extension method for MyClass. However, it will never be called because MyClass.DoThis always takes precedence over it unless you explicitely call it as a static method of MyExtensions01.

In cases where you have two extension methods with the same name, an extension method in the current namespace will win out over one in another namespace. Ambiguity will become an issue, however, when you try to call two extension methods with the same name and signature in the same namespace, or in two different namespaces both used by the current namespace. See Listing 5 for an example of this problem.

The following code will not compile because the compiler finds the call to DoThat ambiguous:

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

namespace ExtensionScope
{
    public class MyClass
    {
        public void DoThis()
        {
            Console.WriteLine("Do this");
        }
    }
}

namespace Extensions01
{
    using ExtensionScope;

    public static class MyExtensions01
    {
        // Can never be called
        public static void DoThis(this MyClass myClass)
        {
            Console.WriteLine("Do this");
        }

        public static void DoThat(this MyClass myClass)
        {
            Console.WriteLine("Do bop");
        }
    }
}

namespace Extensions02
{
    using ExtensionScope;

    public static class MyExtensions02
    {
        // Can never be called
        public static void DoThis(this MyClass myClass)
        {
            Console.WriteLine("Do this");
        }

        public static void DoThat(this MyClass myClass)
        {
            Console.WriteLine("Do bang");
        }
    }
}

namespace ExtensionScope
{
    using Extensions01;
    using Extensions02;

    class Program
    {
        static void Main(string[] args)
        {
            MyClass m = new MyClass();
            m.DoThat();
        }
    }
}

This program throws a compile time error because the compiler does not know if you want to MyExtionsions01.DoThat() or MyExtension02.DoThat() . There are two ways to resolve this error:

  • You could remove the using directive for either Extensions01 or Extensions02. In this case, that would be a fine resolution, but if there were other methods or classes in both Extensions01 and Extensions02 that you wanted to use, then this could become a painful, or even unacceptable, choice.
  • You could explicitly state which method you want to call using standard static syntax: MyExtensions01.DoThat(m) .
  • You could move either MyExtensions02 or MyExtensions01 into the ExtensionScope namespace: 
 namespace ExtensionScope
{
    public static class MyExtensions02
    {
        public static void DoThat(this MyClass myClass)
        {
            Console.WriteLine("MyExtensions02.DoThat");
        }
    }
}

This latter solution works so long as you have access to the source.

It should be clear that some of the issues discussed here can lead to trouble if you are not careful. In particular, you don't want to end up in a situation where forcing someone to remove a namespace results in them losing access to important functionality, nor do you want to force them to choose between functionality they desire and using your extensions.

It can also be a serious nuisance if you muddy a namespace with what many developers might consider superfluous methods. If you added 50 extension methods to the C# string class, then developers who just want to access the base functionality of that object would always have to contend with your methods, particularly when using IntelliSense.

To avoid or at least mitigate the seriousness of these problems, you should always place your extension methods in unique namespace separated from the rest of your code so that you can easily include or exclude the extension methods from a program. Listings 10 and 11 illustrate this technique.

Place your extensions in a separate file, and give them a unique namespace:

 namespace MyCode
{
    public class MyCode
    {
        // Code omitted here
    }
   
}
 namespace MyCode.Extensions
{
    public static class SpecialString
    {
        private static string[] stateCodes = 
                {"AL","AK","AZ","AR","CA","CO","CT","DE","FL",
                 "GA","HI","ID","IL","IN","IA","KS","KY","LA",
                 "ME","MD","MA","MI","MN","MS","MO","MT","NE",
                 "NV","NH","NJ","NM","NY","NC","ND","OH","OK",
                 "OR","PA","RI","SC","SD","TN","TX","UT","VT",
                 "VA","WA","WV","WI","WY"};

        public static bool IsState01(this string source)
        {
            if (source == null) return false;
            source = source.ToUpper(); 
            foreach (var item in stateCodes)
            {
                if (source == item)
                {
                    return true;
                }
            }
            return false;
        }

        public static bool IsState02(this string source)
        {
            return (source == null) ? false : stateCodes.Contains(source.ToUpper());
        }
    }
}

In this code I show you two alternative ways to implement the IsState extension method. The second, which uses LINQ, is probably easier to maintain. You can access the extension methods in a namespace MyCode.Extensions like this:

 using System;
using MyCode;
using MyCode.Extensions;

namespace ConsoleApplication1
{
    class Program
    {            
        static void Main(string[] args)
        {
            MyCode myCode = new MyCode();
            // Use My Code here.
            string test = "WA";
            if (test.IsState02())
            {
                Console.WriteLine("{0} is a state", test);
            }
        }
    }
}

In this code your extension method is available and the code compiles. Comment out the third using statement and your extension method would not be available and the code would not compile. The developer would, however, still have access to the functionality found in the MyCode namespace. You could perhaps improve this technology by putting your extensions in their own assembly with its own namespace. You could then be sure that developers could choose to include or exclude the extra weight of your extension methods when they ship their code.

Though extension methods are particularly useful in LINQ, they are now a part of the language, and if used with caution, they can be useful. Placing them in their own namespace is a best practice that should help you get the most from this feature.

kick it on DotNetKicks.com