Extension Dilema

I was just working on a little Open Source project I have where I need an un-directed graph to represent relationships between people who have and have not met. I have also been working on a little open source project that supplies basic graph algorithms (which can be found here). I figured the best course of action would be to import the library and extend the node class to represent a person. Then I could add my own AddNeighbor and RemoveNeighbor methods. Everything went well until time to run unit tests and I found something peculiar.

The original implementation of Node is very generic and is as follows:

 public class Node<T> 
    {
        public List<Node<T>> Neighbors { get; private set; }

        public T Data { get; private set; }

        public Node(T data)
        {
            if (data == null) throw new ArgumentNullException("data");
            Data = data;
            Neighbors = new List<Node<T>>();
        }

        public void AddNeighbor(Node<T> node)
        {
            if (node == null) throw new ArgumentNullException("node");
            if (!Neighbors.Contains(node))
            {
                Neighbors.Add(node);
            }
        }
    }

The implementation was as easy as adding a RemoveNeighbor method to the Node class:

public void RemoveNeighbor(Node<T> node)
        {
            if (node == null) throw new ArgumentNullException("node");
            if (Neighbors.Contains(node))
            {
                Neighbors.Remove(node);
            }
        }

At this point I could extend Node and write my own add and remove neighbor methods:

public class Person : Node<int>
    {
        public Person(int data) : base (data){}

        public void RemoveNeighbor(Person person1, Person person2)
        {
            person1.RemoveNeighbor(person2);
            person2.RemoveNeighbor(person1);
        }

        public void AddNeighbor(Person person1, Person person2)
        {
            person1.AddNeighbor(person2);
            person2.AddNeighbor(person1);
        }
    }

For a first try I was very pleased with my efforts, but when I went to implement other functionality in the application, I noticed that the method signatures were clunky. I wanted to do something like this:

person1.AddNeighbor(person2);
person1.RemoveNeighbor(person2);

So to implement that, I added two extension methods that looked like the following:

        public static void RemoveNeighbor(this Person person1, Person person2)
        {
            person1.RemoveNeighbor(person2);
            person2.RemoveNeighbor(person1);
        }

        public static void AddNeighbor(this Person person1, Person person2)
        {
            person1.AddNeighbor(person2);
            person2.AddNeighbor(person1);
        }

If you already can see what is going to happen, this compiles just fine. These are valid overloads of the methods. Unfortunately, the compiler assumes that I want the Node’s implementation when I use it as demonstrated as above, since the signatures in the consumer are equivalent, even though I know I want to use the extension methods, so unit tests start failing.

The only way I can see around this is for me to name my extension methods differently. I would be interested to hear from someone who knows how to specify which method to use on the consumers side given that the method signatures are equivalent.
 

Advertisements