David Whitten
David Whitten

Reputation: 199

C# Generics - Mimicking C++ Template Specialization

I've found a nice link on C++ Tenmplates:

http://www.cplusplus.com/doc/tutorial/templates/

and needed something similar in C#. I have a solution that seems to work but wanted opinions of others in how it relates to the above link, specifically the specialization section.

Here is a proof of concept I came up with:

public abstract class Piece
{
    public object Value { get; set; }
}

public class Rook : Piece
{
    public void Capture()
    {
        int i = (int)this.Value;
    }
}

public class Pawn : Piece
{
    public void CaptureEnPassant()
    {
        string s = (string)this.Value;
    }
}

public class PieceFactory<P, T> where P : Piece, new()
{

    P p;

    public PieceFactory(T value)
    {
        p = new P();
        p.Value = value;
    }

    public P GetPiece()
    {
        return p;
    }
}

and then finally to call into the factory I do this:

var piece = new PieceFactory<Pawn, string>("exd6").GetPiece();

piece.CaptureEnPassant();

I've seen different solutions like using extension methods and other ways...

Just wanted to see if my way of thinking is along the lines of good patterns.

THanks so much,

David

Upvotes: 1

Views: 647

Answers (3)

sehe
sehe

Reputation: 393009

FYI I tried converting my own template-programmed chess engine into C# for fun, and found it was slower by roughly a factor of 20 across the board [sic].

That includes stuff like parsing the gamefile format. Position lookup and move generation just had a lot of mechanical sympathy in the C++ version, that applying all the tricks could not make up for:

  • compiletime optimization
  • non-shared generics (mono specific - see here, e.g.)
  • unsafe code (pinned arrays, raw pointers),
  • unchecked blocks (as in array bounds/arithmetic overflow
  • value typed arrays and ref passing
  • short, inlinable functions
  • garbage prevention (custom allocation in preallocated 'realms' (just large arrays of structs preallocated)

That said, the performance benefit from using generic collections is significant, esepcially for, say List<T> where T : struct. Note however, the caveats from the link above (especially for the new constraint which has rather pathetic performance on MS. NET due to code sharing; it is basically as slow as using reflection to call the constructor, even for value types).

YMMV, but in the end I'd say

1. Go with the C# way. If you must optimize, do it the C# way

2. If all else fails, resort to P/Invoke
   (C++/CLR is a sweet spot if you target Windows)

Upvotes: 1

Eric Lippert
Eric Lippert

Reputation: 660058

My opinion is that your sketch is far more complex and confusing than necessary. Why does the base class have a "value" that has different meanings and different types in each derived class? Why is there a factory that takes a type parameter that must be of a particular type argument, and if it is not, then the program crashes at runtime? Get rid of all that brittle, confusing, fragile stuff. If a pawn needs a string, then make a public constructor on Pawn that takes a string, end of story. There's no need for the factory pattern at all here.

Don't be so in love with the tool that you build stuff out of it that doesn't actually make any sense. Generic types are great for things like collection classes. They're not a good fit for the chess domain.

Upvotes: 8

as-cii
as-cii

Reputation: 13019

I would just use generics on your base class. Does this break something in your code?

void Main()
{
    var factory = new PieceFactory<Rook, int>(20);
    factory.GetPiece().Dump();
}

abstract class Piece<TValue>
{
    public TValue Value { get; set; }
}

class Rook : Piece<int>
{
    public int Capture()
    {
        // Do something...
        return base.Value;
    }
}

class Pawn : Piece<string>
{
    public string EnPassant()
    {
        // Do something...
        return base.Value;
    }
}

class PieceFactory<TKey, TValue> where TKey : Piece<TValue>, new()
{
    private readonly TKey piece;

    public PieceFactory(TValue value)
    {
        this.piece = new TKey();
        this.piece.Value = value;
    }

    public TKey GetPiece()
    {
        return this.piece;
    }
}

I have also put some access keywords (like this and base) and a readonly modifier in your factory.

Upvotes: 0

Related Questions