One of the things C# generics lacks (compared to C++ templates) is specialization (neither explicit nor partial). This can be very useful in some cases where you want to perform something differently for a specific T in a Class<T>.

With C# 3.0, there is a relatively easy way to achieve this, albeit not optimal, for reasons I'll specify later on. Take a look at the following piece of code:

 

    public class SomeClass<T>

    {

        private readonly List<T> items = new List<T>();

 

        internal void AddInternal(T item)

        {

            items.Add(item);

        }

    }

 

    [EditorBrowsable(EditorBrowsableState.Never)]

    public static class SomeClassExtensions

    {

        public static void Add<T>(this SomeClass<T> c, T item)

        {

            c.AddInternal(item);

        }

 

        public static void Add(this SomeClass<int> c, int item)

        {

            if (item <= 0)

            {

                throw new ArgumentOutOfRangeException("item", "Value must be a positive integer.");

            }

            c.AddInternal(item);

        }

    }

 

Instead of placing the Add method in SomeClass<T>, we put it in a static class that has extension methods. This allows the compiler to select the appropriate method according to the type parameter. (Side note: I've specified the EditorBrowsable attribute so that the class with the extensions would not appear in VS intellisense.)

The biggest caveat about this is that extension methods do no have access to private members, so the only option is to make the members internal, which, in many cases, leads to a bad design. If only extension method classes could be written as inner classes…

Also, extension methods, being static, could not be virtualized. You can, however, make the internal method "protected internal virtual".

Tagged with:
 

4 Responses to C# Partial Specialization With Extension Methods

  1. Peter Verswyvelen says:

    Hi aelij,

    Nice hack! Will come in handy.

    But it works only partially.

    E.g. when you try it with the following program:

    static void main()

    {

     var c = new SomeClass<float>();

     c.Add((float)-1);

     c.Add((int)-1);

    }

    the generic Add method is always called (the int is silently converted to a float?), so no specialization takes place as in C++.

  2. aelij says:

    Hi Peter,

    It’s not supposed to work. The specialization is for an instance of SomeClass<T>, not for each call to the Add() method. So, if you create a SomeClass<int>, the compiler will select the most specialized method.

    Aelij.

  3. Ernst-Jan says:

    Hi aelij,

    Nice solution, but I think there is an issue if you try to use it from another generic function. For instance the code below will not call the overload:

    void foo<T>(T t)

    {

      SomeClass<T> o = new SomeClass<T>();

      0.Add(t);

    }

    static void main()

    {

      foo<int>(3); // calls SomeClassExtensions<T> instead of override

    }

    I think a better approach is the following:

       class SomeType

       {

       }

       class Foo<T>

       {

           public static Foo<T> CreateInstance()

           {

               if (typeof(T) == typeof(SomeType)) return new FooSomeType() as Foo<T>;

               return new Foo<T>();

           }

           protected Foo()

           { }

           public virtual void SomeMethod()

           {

               // what ever

           }

           private class FooSomeType : Foo<SomeType>

           {

               public override void SomeMethod()

               {

                   // SomeType specific handling goed here

               }

           }

       }

  4. Aman says:

    good one

Set your Twitter account name in your settings to use the TwitterBar Section.