C# generic constraints : Interface

I got a question for some code:

interface IDistance<T> 
{
        double distance();
        double distance(T obj);
}

class Point<T> where T : IDistance<T>  //why do i need this?
{
        T obj;
        public double dist(T val) { return obj.distance(val); 
        public Point(T obj) { this.obj = obj; }
}

class P2D : IDistance<P2D>
{
        public double[] x = new double[2];

        public P2D(double x, double y)
        {
            this.x[0] = x; this.x[1] = y;
        }

        public double distance()
        {
            double d = 0.0;
            for (int i = 0; i < 2; i++) d = d + x[i] * x[i];
            return Math.Sqrt(d);
        }

        public double distance(P2D val)
        {
           double d = 0.0;
           for (int i = 0; i < 2; i++) d = d + Math.Pow(x[i]-val.x[i],2);
           return Math.Sqrt(d);
        }
}

class Tester
{
        static void Main(string[] args)
        {
            P2D P1 = new P2D(3.0, 4.0);
            Point<P2D> C1 = new Point<P2D>(P1);
            Console.WriteLine(C1.dist());
        }
} 

The code in detail is rather unimportant.

Why do I need the constrain where T : IDistance<T> in the generic class Point<T>?

When I only specify classes that already implemented the interface IDistance<T> like Class P2D, shouldn't be the interface already implemented implicit in the class Point?

I get the fact that it can cause problems, when a class as type <T> in class Point is defined that has not implemented the interface. But in this case, why is it not possible?

Jon Skeet
people
quotationmark

Look at this code within Point<T>:

T obj;
public double dist(T val) { return obj.distance(val); 

When the compiler tries to understand what this expression means:

obj.distance(val)

it has to resolve the distance member. If T is unconstrained, it can't do that. When T is constrained to implement IDistance<T>, it can - it resolves it to the member of the interface.

In particular, without the constraint, I could use the type in very odd ways:

Point<string> weird = new Point<string>("foo");
double result = weird.dist("bar");

What would you expect that to do?

(As a side note, it would be worth following normal .NET naming conventions, even for examples. Methods should be PascalCased, and I'd never call a class P2D...)

people

See more on this question at Stackoverflow