Wednesday, June 16, 2010

C# Puzzle No.20 (intermediate)

Suppose you are writing a class for a generic vector and you want to naively implement a method to add two vectors:

public class Vec<T>
{
    public T x { get; set; }
    public T y { get; set; }
 
    public Vec() { }
    public Vec( T x, T y ) { this.x = x; this.y = y; }
 
    public static Vec<T> Add( Vec<T> v1, Vec<T> v2 )
    {
        return new Vec<T>( v1.x + v2.x, v1.y + v2.y );
    }
}

This won’t work. The compiler is unable to interpret the “+” for type T so v1.x + v2.x cannot be compiled.

One of possible approaches could be to restrict possible Ts with a type constraint so that “+” can be replaced with a method of an interface implemented by T. Such approach, however, does not take you anywhere since you cannot impose an interface on an existing type and thus, Vec<int> or Vec<double> would still fail to compile.

Another common approach is to define an auxiliary interface:

public interface IAdder<T>
{
    T Add( T t1, T t2 );
}

so that Vec::Add can be now implemented as:

public static Vec<T> Add( Vec<T> v1, Vec<T> v2, IAdder<T> Adder )
{
    return new Vec<T>( Adder.Add( v1.x, v2.x ), Adder.Add( v1.y, v2.y ) );
}

and used:

public class IntAdder : IAdder<int>
{
    public int Add( int t1, int t2 )
    {
        return t1 + t2;
    }
}
 
class Program
{
    static void Main( string[] args )
    {
        Vec<int> v1 = new Vec<int>( 4, 5 );
        Vec<int> v2 = new Vec<int>( 6, 7 );
 
        Vec<int> v3 = Vec<int>.Add( v1, v2, new IntAdder() );
 
        Console.ReadLine();
    }
}

While this works, there are obvious drawbacks – an extra implementation of the auxiliary interface has to be provided for any T you want to use your Vec class with. Also, the signature of the Add method is now rather clumsy.

Let’s then just start again:

public class Vec<T>
{
    public T x { get; set; }
    public T y { get; set; }
 
    public Vec() { }
    public Vec( T x, T y ) { this.x = x; this.y = y; }
 
    public static Vec<T> Add( Vec<T> v1, Vec<T> v2 )
    {
       // ???
    }
}

Your goal is to provide valid implementation of the Add method so that the returning vector is a sum of two provided vectors and the generic class works for built-in numeric types. You are not allowed to use reflection (or any BCL API) but rather depend solely on the features of the C# language.

2 comments:

vveasel said...

public class Vec
{

public T x { get; set; }

public T y { get; set; }

public Vec() { }

public Vec(T x, T y) { this.x = x; this.y = y; }

public static Vec Add(Vec v1, Vec v2)
{
dynamic a = v1;
dynamic b = v2;
return new Vec(a.x + b.x, a.y + b.y);
}

public override string ToString()
{
return x + " " + y;
}
}


class Program
{
static void Main(string[] args)
{
Vec a = new Vec(1,2);
Vec b = new Vec(3, 4);
Console.WriteLine(Vec.Add(a,b));
}
}

Is it correct?

Wiktor Zychla said...

yes indeed. the trick is to use dynamics. regards.