jmodrak
jmodrak

Reputation: 229

C# : Generic factory object instantiation by passing type (type retrieved from another static object)

I created a little abstract domain to illustrate the problem I am facing, so there it is.

There is a medieval game, where the players are generals of their army and the entire battle is mostly affected by the battle plan, which is made before the battle begins, in let's say preparation mode.

To achieve what's needed, I created an interface IBattleUnit and kept things pretty simple:

public interface IBattleUnit
{
    void Move();
    void Attack();
    string Salute();
}

Having three types of units will do the job for now, so Archer.cs, Pikeman.cs and Swordsman.cs implement the interface in pretty much the same way:

public class Swordsman : IBattleUnit
{
    private Swordsman() {}

    public void Move()
    {
        //swordsman moves
    }

    public void Attack()
    {
        //swordsman attacks
    }

    public string Salute()
    {
        return "Swordsman at your service, master.";
    }
}

Note the private constructor, it is intended for battle units to be recruited only in Barracks, this is the generic factory

public static class Barracks<T> where T : class, IBattleUnit
{
    private static readonly Func<T> UnitTemplate = Expression.Lambda<Func<T>>(
       Expression.New(typeof(T)), null).Compile();

    public static T Recruit()
    {
        return UnitTemplate();
    }
}

Note: precompiled lambda expressions for the empty constructor make (on my machine) unit creation faster, and whereas the army can get really big, fast generic creation is exactly what I want to achieve.

For having covered everything a battle needs to be started, the BattlePlan explanation is the only missing part, so here we come:

public static class BattlePlan
{
    private static List<Type> _battleUnitTypes;
    private static List<Type> _otherInterfaceImplementors;
    //...

    private static Dictionary<string, string> _battlePlanPreferences;

    private static Type _preferedBattleUnit;
    private static Type _preferedTransportationUnit;
    //...

    static BattlePlan()
    {
        //read the battle plan from file (or whereever the plan init data originate from)
        //explore assemblies for interface implementors of all kinds
        //and finally fill in all fields
        _preferedBattleUnit = typeof (Archer);
    }

    public static Type PreferedBattleUnit
    {
        get
        {
            return _preferedBattleUnit;
        }
    }

    //... and so on

}

Now if you have reached this, you are aware of the whole domain - it even compiles and everything looks bright, until...

Until now: I create a console application, add references to the above mentioned, and try to profit from what's under the hood. For complete description of my confusion, I note what IS WORKING first:

IBattleUnit unit = Barracks<Pikeman>.Recruit();
Type preferedType = BattlePlan.PreferedBattleUnit;

And here, when I expect the BattlePlan to supply me with a Type and me just passing the Type to Barracks in order to instantiate some kind of Unit, VisualStudio2012 (resharper of current version) stops me and does not compile the code, while the code, that leads to the error is:

Type t = Type.GetType(BattlePlan.PreferedBattleUnit.AssemblyQualifiedName);
IBattleUnit u = Barracks<t>.Recruit();

No matter what I do, no matter whether I pass the t, or pass it as typeof(t), or try converting it to IRepository ... I still end up not being able to compile such code, with (at least) two errors in the error list:

Error   1   Cannot implicitly convert type 't' to 'BattleUnits.cs.IBattleUnit' Program.cs
Error   2   The type or namespace name 't' could not be found (are you missing a using directive or an assembly reference?) Program.cs

So to the actual questions:

  1. Is there some way, I could pass the type to Barracks, not having to change underlying infrastructure?
  2. Or is there anything I am doing wrong by design?

I have spent the last two days googling around and still, with the only clear way being changing the Barracks, which in fact is what I would not want to.

EDIT no.1: When re-thinking the concept and everything : IBattleUnit was first described as a set of core battle actions every Unit will be able to do (and we want it to be this way). I did not want to introduce base classes, just because I knew, there could possibly be GroundUnitBase and FlyingUnitBase abstract classes for the sake, we would like to have clear and logical design... But there absolutely has to be only one static Barracks.

Still for the BattleUnits - putting one base class in my eyes now seems could change the things for code being runnable and I'm right on my way of trying that out ... reading, what I wrote made me think about UnitBase class could possibly help not even the design but in some way its compilability. So this is the first idea in my mind after rethinking what's written.

Upvotes: 4

Views: 1633

Answers (5)

Magus
Magus

Reputation: 1312

My strategy would be to create a Dictionary<Type, Barracks<IBattleUnit>>, assuming you intend to have all the barracks defined before you try to retrieve from them. That way you can match by the key and cast safely.

This would require the Barracks<> to not be a static class. Unless you have very specific reasons like some kind of external resource you're managing (and arguably even then), you probably have no need for a static class.

While it may seem like creating statics for all of these will make everything easier, ultimately you create a dependency on a resource that may change. If you invent another unit type, you have to register it with the barracks, which is in no real way different than the reason you don't want to make base classes, and if you forget you'll throw exceptions, which is even worse, because it violates the Principle of Least Surprise.

Upvotes: 0

Leonardo
Leonardo

Reputation: 2225

You don't really need Barracks to be generic. This solution doesn't use reflection so it's much more efficient:

public static class Barracks
{
    private static readonly IDictionary<Type, Func<IBattleUnit>> FactoryMethods = new Dictionary<Type, Func<IBattleUnit>>();

    public static void Register<T>(Func<IBattleUnit> factory) where T : IBattleUnit
    {
        FactoryMethods.Add(typeof(T), factory);
    }

    public static IBattleUnit Recruit<T>() where T : IBattleUnit
    {
        return Recruit(typeof (T));
    }

    public static IBattleUnit Recruit(Type type)
    {
        Func<IBattleUnit> createBattleUnit;
        if (FactoryMethods.TryGetValue(type, out createBattleUnit))
        {
            return createBattleUnit();
        }

        throw new ArgumentException();
    }
}

public class Swordsman : IBattleUnit
{
    static Swordsman()
    {
        Barracks.Register<Swordsman>(() => new Swordsman());
    }
}

Upvotes: 1

Moslem Ben Dhaou
Moslem Ben Dhaou

Reputation: 7005

If you have an instance of the PreferedBattleUnit you simply need to use the dynamic keyword. Please have a look at this question (John Skeet answer): (EDIT: This might not be very helpful as your method is not generic)

Pass concrete object type as parameter for generic method

If you don't have an instance of the object than have a look at the following question (again, John Skeet answer):

Generics in C#, using type of a variable as parameter

Upvotes: 1

mayabelle
mayabelle

Reputation: 10014

You can do this using reflection, something like this:

IBattleUnit unit = typeof(Barracks).GetMethod("Recruit").MakeGenericType(BattlePlan.PreferedBattleUnit).Invoke(null, null) as IBattleUnit;

Upvotes: 1

smartcaveman
smartcaveman

Reputation: 42246

public static class Barracks
{
    public static IBattleUnit Recruit(Type preferredType)
    {
        return (IBattleUnit)typeof(Barracks<>).MakeGenericType(preferredType).GetMethod("Recruit", BindingFlags.Public|BindingFlags.Static).Invoke(null,null);
    }
}

then call

Barracks.Recruit(BattlePlan.PreferredBattleUnit)

Upvotes: 1

Related Questions