The Inquisitive Coder – Davy Brion's Blog

Trying to walk that thin line between intelligence and ignorance

Using The Dynamic Keyword To Avoid Difficulties With Generics

Posted by Davy Brion on July 16th, 2010

A coworker was working on some kind of base EntityBuilder class to use in his tests.  One of the requirements of the EntityBuilder class was that it would need to automatically set the ID of an entity to a ‘real’ value (as in: not the default value of the type).  The EntityBuilder would use some kind of IdGenerator based on the type of the ID of the entity.   First of all, the example i’m gonna show is highly simplified and might not look like it makes much sense, but it’s only to illustrate some C# stuff with regards to generics and the dynamic keyword.  So bear with me, and just focus on the language details :)

Suppose you’ve got something like this:

    public abstract class Entity<TId>

    {

        public virtual TId Id { get; set; }

    }

 

    public interface IIdGenerator<TId>

    {

        TId GenerateId();

    }

 

    public class IntIdGenerator : IIdGenerator<int>

    {

        private static int lastIssuedId;

 

        public int GenerateId()

        {

            return ++lastIssuedId;

        }

    }

 

    public class GuidIdGenerator : IIdGenerator<Guid>

    {

        public Guid GenerateId()

        {

            return Guid.NewGuid();

        }

    }

 

The idea was to write the EntityBuilder somewhat along these lines:

    public abstract class TestEntityBuilder<TEntity, TId> where TEntity : Entity<TId>

    {

        public TEntity Build()

        {

            var entity = CreateEntityWithDefaultProperties();

            entity.Id = GetIdGeneratorFor(typeof(TId)).GenerateId();

            return entity;

        }

 

        protected abstract TEntity CreateEntityWithDefaultProperties();

 

        private IIdGenerator<TId> GetIdGeneratorFor(Type type)

        {

            if (type == typeof(int))

            {

                return new IntIdGenerator();

            }

 

            return new GuidIdGenerator();

        }

    }

 

Of course, that doesn’t even compile… you’ll get the following compiler errors:

error CS0266: Cannot implicitly convert type ‘MyProject.IntIdGenerator’ to ‘MyProject.IIdGenerator<TId>’. An explicit conversion exists (are you missing a cast?)
error CS0266: Cannot implicitly convert type ‘MyProject.GuidIdGenerator’ to ‘MyProject.IIdGenerator<TId>’. An explicit conversion exists (are you missing a cast?)

So, how exactly do you get this working with generics? That’s when he asked for my help, and i didn’t know the answer either… i’ve struggled with this exact problem in a few previous situations and i never really got a clean solution either.  But then i thought “wait, can’t we just avoid the problems with generics through the dynamic keyword?”

We changed the code to look like this:

    public abstract class TestEntityBuilder<TEntity, TId> where TEntity : Entity<TId>

    {

        public TEntity Build()

        {

            var entity = CreateEntityWithDefaultProperties();

            entity.Id = GetIdGeneratorFor(typeof(TId)).GenerateId();

            return entity;

        }

 

        protected abstract TEntity CreateEntityWithDefaultProperties();

 

        private dynamic GetIdGeneratorFor(Type type)

        {

            if (type == typeof(int))

            {

                return new IntIdGenerator();

            }

 

            return new GuidIdGenerator();

        }

    }

 

We just changed the return type of the GetIdGeneratorFor method to ‘dynamic’, and the call to the GenerateId method is now a dynamic call instead of a normal method call.  And it works.  No messing around with generics voodoo, no (direct) usage of reflection either.  Just clean code.

I’ll probably use this trick a lot more times in the future when i run into the limitations of generics :)

15 Responses to “Using The Dynamic Keyword To Avoid Difficulties With Generics”

  1. Tommy Carlier Says:

    First of all, the GetIdGenerator-function doesn’t really need the type-parameter: it already knows that (typeof(TId)).

    Second, the error message gives a tip of how you can solve it: you can cast it explicitely, like this:

    private static IIdGenerator<TId> GetIdGenerator()
    {
    return typeof(TId) == typeof(int)
    ? new IntIdGenerator() as IIdGenerator
    : new GuidIdGenerator() as IIdGenerator;
    }

  2. Adam Says:

    In this particular case I would consider using IoC for looking up generators. The GetIdGeneratorFor method is like a lookup call.
    Otherwise I see how the dynamic keyword can help you avoid problems in similar cases. Neat solution, lot less ugly than writing casts all around, even though it lacks type safety in the same way.

  3. Davy Brion Says:

    @Tommy

    there is no IIdGenerator type… and AFAIK, you can’t cast it explicitly to a generic instance of IIdGenerator of the correct type (either long or guid)

    so your code wouldn’t compile either

    @Adam

    yeah you could… but this class was meant to be used during testing so maybe IOC is a bit over the top then ;)

  4. Tommy Carlier Says:

    A better design could be to create a static dictionary with IdGenerators (so it’s easier to add types in the future):

    private static readonly IDictionary<Type, object>
    fIdGenerators = InitializeIdGenerators();

    private static IDictionary<Type, object> InitializeIdGenerators()
    {
    var generators = new Dictionary<Type, object>();
    generators.Add(typeof(int), new IntIdGenerator());
    generators.Add(typeof(Guid), new GuidIdGenerator());
    return generators;
    }

    private static IIdGenerator<TId> GetIdGenerator()
    {
    object generator;
    return fIdGenerators.TryGetValue(typeof(TId), out generator)
    ? generator as IIdGenerator<TId>
    : null;
    }

  5. Tommy Carlier Says:

    @Davy, the < and > must not have come through; I meant this:
    private static IIdGenerator<TId> GetIdGenerator()
    {
    return typeof(TId) == typeof(int)
    ? new IntIdGenerator() as IIdGenerator<TId>
    : new GuidIdGenerator() as IIdGenerator<TId>;
    }

  6. Davy Brion Says:

    @Tommy

    huh… i could’ve sworn we’ve tried that, but you’re right… that works :)

    though i still prefer the dynamic version :p

  7. pho Says:

    Tommy’s suggestion is what came to mind here too; was getting ready to reply until i figured out the solution was already here ;-)

    Why do you prefer the dynamic version though? I would even say it’s utterly useless here

  8. Davy Brion Says:

    I just think it’s kinda stupid to repeat the same cast in both branches even though you already know it’s of the correct type

  9. pho Says:

    Since it’s a private method so its scope is pretty limited i guess it’s debatable.
    But to be consequent, returning a dynamic when it’s definitely possible to return a specific type (or relevant abstraction thereof) feels wrong

  10. Mark Knell Says:

    It’s overkill for this particular problem, but I’ve sometimes found it useful to declare explicit types for primary keys. E.g.,

    // marker interface
    public interface IKey {}

    public struct IntKey : IKey {…}

    public struct GuidKey : IKey {…}

    Now you can be generic on IKey.

    If you want to be relaxed about it, you can provide implicit conversion operators to go to/from the underlying types. But if you keep things strict, you can do stuff like make your keys immutable.

    Not simple to introduce to an existing app, though.

  11. Davy Brion Says:

    @Pho

    i wouldn’t say that it’s wrong… i think most of us can agree by now (given the success of weakly typed languages over the past couple of years) that weak typing can’t be as bad as we were always told it was

    and yeah, it is possible (apparently) to return the specific type in this situation… but even then it just feels so cumbersome to do so. You’re basically paying lip service to the compiler and that is truly the only reason why you’d use that cast.

  12. Julian Birch Says:

    The cast idea can be made to work, and it’s not that bad. I mean, it may look ugly, but it’s hardly inefficient: it’s going to do that cast anyway.

    However, in general terms what you’re doing is just plain nasty. You could make the get generator method abstract and have subclasses for int and guid, or take the generator as a constructor dependency. In production code I’d probably favour the latter (NH anyone?) but the convenience of the former is likely to be a win for testing code.

    And I’ve no particular objection to using dynamic, it just feels like overkill.

  13. frederikm Says:

    Hmz, i don’t get why you have to use dynamic..
    if the method needs to have an idication of the type of id, just pass in your entity

    public abstract class TestEntityBuilder where TEntity : Entity

    {
    public TEntity Build()
    {
    var entity = CreateEntityWithDefaultProperties();
    entity.Id = GetIdGeneratorFor(entity).GenerateId();

    return entity;

    }

    private IIdGenerator GetIdGeneratorFor (TEntity entity) where TEntity : Entity

    {
    if (type == typeof(int))
    {
    return new IntIdGenerator() as IdGenerator;
    }

  14. frederikm Says:

    hmz, looks like the generic tags disappeared..

    it’s off course iidgenerator <Id> and GetIdGenerator <Tentity >

  15. Stacy Says:

    I’ve found it useful to make my constructors private and expose a static factory method that returns an interface, it forces use of the interface and takes care of the stupid ternery casting problem.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>