Monday, October 24, 2011

The Danger of Extension Methods

Today I came across something with extension methods that can cause code to break.  If an extension method’s signature is equivalent to an actual method on the class or interface the extension method will not be called.  For example let say I have the following classes:
    public class Foo
    {
        public object FindItem(int id)
        {
            //Some implimentation
        }
    }

    public static class FooExtensions
    {
        public object FindItem(this Foo foo, int id)
        {
            //Some slightly different implementation.
        }
    }

The FindItem in FooExtensions will never be called unless you call it as FooExtensions.FindItem(foo, id);.  In general this is not a horrible problem as you should avoid naming extension methods the same as an existing method.  The problem comes in if someone extends or modifies a class so that the extension method was hidden.  I discovered this the hard way.  I had a class setup that looked like:
    public class Foo
    {
        public object GetItem(int id)
        {
            //Some implimentation
        }
    }

    public static class FooExtensions
    {
        public object FindItem(this Foo foo, int id)
        {
            //Some slightly different implementation.
        }
    }


GetItem and FindItem had subtly different behavior in how they returned a value.  In 90% of the cases the two methods behaved the same.  However, there was one case where they didn’t.  During a refactor of some code I was trying to unify the language in the Foo interface and renamed GetItem to be FindItem.  I didn’t realize that there was an extension method elsewhere in the code that this change would hide. 
Inevitably, this change broke in the one case where the two methods did not behave in the same manner.  However, it was unclear as to why the code broke.  I spent a good hour looking at the broken code and could not determine why it ever worked.  Since the extension method is hidden by the class’s method of the same name it does not appear when trying to find references.  After searching for a solution to no avail, I made a change to the code call to what used to be the extension method version of FindItem.  Interestingly, this made the code more efficient.  It was only a month later i.e. today when I came across the extension method and found no references to it that I realized what had happened. 

This experience has led me to some thoughts about good coding practices to avoid this issue.
  • Make sure all your extension methods have unit tests associated with them.  This issue would have been spotted in a unit test for the extension method if a test for it had existed.  My project doesn't use a TDD approach and we don’t have full coverage so this error was not caught at this level.  A good unit test would have caught the case that caused the error.  This is the most straightforward and safest way to prevent the error.
  • Collect the extension methods in a place that makes them easy to find.  They probably should be within their own namespace.  Part of the reason this error wasn't caught was that the offending extension method was in a general namespace that was referenced for other reasons.  If a namespace suddenly was no longer needed, this would have been a clue about the cause of the problem.
  • Be judicious with the use of extension methods.  The FindItem method should have been part of Foo.  It was a mistake to not put it there in the first place.  Extension methods are really tempting for people who come from a procedural programming background.  However, they should only be used as a mechanism to extend functionality when the code cannot be extended otherwise.  They should not be used as way of not adding a method to an object or just providing a method that should be a static method to the class.

10 comments:

  1. for beginners like me need a lot of reading and searching for information on various blogs. and articles that you share a very nice and inspires me .
    cara menggugurkan kandungan
    obat aborsi
    kalkulator kehamilan

    ReplyDelete
  2. Occasionally you logdown.com/ just do not intend to play a More Info video game or make use of an Machine Liker Download and Install on PC, APK Android Free application on an and also Mac to mount Machine Liker App BlueStacks if you're making use of a Mac PC or Laptop. Machine Liker Greater than Facebook.

    ReplyDelete
  3. As specified over, the Revolution splashthat.com/ SE Single from BOB includes a flexible suspension system Website that is indicated to enhance safety and security and Best Jogging Stoller in June 2017 safety for your youngster.

    ReplyDelete