List of generic objects

Started by
10 comments, last by Pink Horror 9 years, 7 months ago

I'm trying to implement a list of a generic collection and am really confused on how to actually add values to the list in C#. Here is some code demonstrating what I'm trying to do. I am having trouble adding a generic object to a list of generic objects. I've looked all over online and haven't found anything helpful or something that works.


public class Creature
{

}

public class Goblin : Creature
{
	public int sillyValue = 5;
}

public class Troll : Creature
{
	public string name = "bob";
}


public class Unit<T> where T : Creature
{
	List<T> critters = new List<T>();

	public void Add(T item)
	{
		critters.Add(item);
	}

	public Type Type
	{
		get { return typeof(T); }
	}
}

public class Army
{
	List<Unit<Creature>> units = new List<Unit<Creature>>();

	public void Add<T>(Unit<T> unit) where T : Creature
	{
		units.Add(unit);	//<-- SYNTAX ERROR: The best overloaded method match for 'System.Collections.Generic.List<UnitTester.Unit<UnitTester.Creature>>.Add(UnitTester.Unit<UnitTester.Creature>)' has some invalid arguments
	}
}

class Program
{
	static void Main(string[] args)
	{
		Army army = new Army();

		Unit<Goblin> goblins = new Unit<Goblin>();
		goblins.Add(new Goblin());

		Unit<Troll> trolls = new Unit<Troll>();
		trolls.Add(new Troll());

		army.Add(goblins);
		army.Add(trolls);
	}
}

Everything works, except for that one line in the army.add(). If I change the Army class to this, adding to a list works but now I can't access my creature specific virtual methods because casting a derived object to its base class fails. I can get the specific type of the object, but I don't know how to convert it into its actual class object type.


public class Army
{
	List<object> units = new List<object>();

	public void Add<T>(Unit<T> unit) where T : Creature
	{
		units.Add(unit);
	}
	
	public void DoSomething()
	{
		for(int a=0;a<units.Count;a++)
		{
			Creature c = (Creature)units[a]; //<-- casting fail: Unable to cast object of type 'UnitTester.Unit`1[UnitTester.Goblin]' to type 'UnitTester.Creature'.
			c.print();
		}
	}
}

public class Creature
{
	public virtual void print()
	{
		Console.Write("Hello world");
	}
}

public class Goblin : Creature
{
	public int count = 5;

	public override void print()
	{
		Console.Write("value is: " + count);
	}
}

public class Troll : Creature
{
	public string name = "bob";

	public override void print()
	{
		Console.Write("name is: " + name);
	}
}

I found this article on MSDN which looks very close to what I'm trying to do, but it wasn't very helpful: How to: Examine and Instantiate Generic Types with Reflection

Any help or insight would be much appreciated :)

Advertisement
If this is what you want...

- Unit to only contain one specific type of Creature.
- Armies to contain Units, where different Units can have different Creatures.

...then this is what I would do:

- Make a base "Unit" class which is not generic.
- Make a "Unit<T>" class which derives from Unit.
- Have Army contain a List<Unit> instead of List<Unit<T>>.

NOTE: You can still break a Unit's single-type nature by adding creatures of a more derived type than T. It only prevents you from adding Creatures of a more basic type. If your Creature hierarchy is entirely abstract and sealed classes, then this will work fine.


Instead of using generics, you can enforce a specific, single Creature type in a different manner:

- Remove the generic T from the Unit completely (same as first suggestion).
- Give the Unit a "public readonly Type Type;" member which is set by the constructor.
- Change the Unit's List to List<Creature> (same as the first suggestion).
- Make your Unit's Add method only allow the exact type you specified in the ctor: (item.GetType() == Type)
- Make sure you cannot bypass the Add method (you can make Unit implement IList and restrict access to the internal collection - personally I would write my own collection separate from Unit if this kind of type safety was a major concern).


Lastly, if you don't actually care about having a single-Creature-type Unit restriction, then just remove the generics from your own classes and methods entirely:

- Unit.critters : List<Creature>
- Army.units : List<Unit>

public void Add<T>(Unit<T> unit) where T : Creature
{
  units.Add(unit); //<-- SYNTAX ERROR
}


`units` is a list of `Unit<Creature>`. `unit` is a `Unit<T>`. A `Unit<T>` is not the same thing as a `Unit<Creature>`, even with the type constraint, since you could also pass in a `Unit<Goblin>` which is not compatible with a `Unit<Creature>`.

C# supports the notion of covariance and contravariance in generics. This is a way of indicating that a generic object of a derived or super type is compatible.

However, I see no reason why you need the constraints in the first place. Do you really need to ensure that a `Unit` only contains one type of `Creature`? Do you _really_ need to use inheritance and derived classes to define different creatures rather than using data and member variables on a single type (do they - from a functional/computational standpoint - differ in behavior)?

Your problems will be solved by abandoning generics for this use case (it's not an appropriate or meaningful use of generics) and using data-driven design and component-based design.

Sean Middleditch – Game Systems Engineer – Join my team!

However, I see no reason why you need the constraints in the first place. Do you really need to ensure that a `Unit` only contains one type of `Creature`? Do you _really_ need to use inheritance and derived classes to define different creatures rather than using data and member variables on a single type (do they - from a functional/computational standpoint - differ in behavior)?


Your problems will be solved by abandoning generics for this use case (it's not an appropriate or meaningful use of generics) and using data-driven design and component-based design.

In the game, a unit is a collection of a specific type of creatures. So, I could have a unit of 25 archers, a unit of 40 swordsmen, a unit of 10 peasants, etc. I don't want to be able to mix and match creature types in a unit, so a unit shouldn't have 15 archers and 10 peasants. An army is a collection of units, so if you want to mix and match creatures, you create various unit types and add them to the army (one unit of 15 archers, another unit of 10 peasants).

The bolded portion is a really good question and point for me to ponder. It's certainly something worth keeping in mind when contemplating whether to create inherited classes.

In the code I posted above, the creature types were more for illustration of concept than for usage in practice.

In my actual game though, the player wizard is an object which derives from the creature class. The player wizard can do all sorts of things which a standard creature doesn't do (like channel mana and cast spells) and has a whole bunch of attributes which no other creature needs to worry about (age, ghost state, etc). So, it would make sense for the wizard to be a derived class from Creature. If I were to add a simple peasant, it wouldn't need a distinct class and could just be an instance of Creature with its specific distinctions being set in the Creature properties. Who knows what other creatures and special rule sets I'll need? Whatever it eventually becomes, the architecture should be robust enough to support it with as little change to code as possible.


If this is what you want...

- Unit to only contain one specific type of Creature.
- Armies to contain Units, where different Units can have different Creatures.

...then this is what I would do:

- Make a base "Unit" class which is not generic.
- Make a "Unit<T>" class which derives from Unit.
- Have Army contain a List<Unit> instead of List<Unit<T>>.

NOTE: You can still break a Unit's single-type nature by adding creatures of a more derived type than T. It only prevents you from adding Creatures of a more basic type. If your Creature hierarchy is entirely abstract and sealed classes, then this will work fine.


Instead of using generics, you can enforce a specific, single Creature type in a different manner:

- Remove the generic T from the Unit completely (same as first suggestion).
- Give the Unit a "public readonly Type Type;" member which is set by the constructor.
- Change the Unit's List to List<Creature> (same as the first suggestion).
- Make your Unit's Add method only allow the exact type you specified in the ctor: (item.GetType() == Type)
- Make sure you cannot bypass the Add method (you can make Unit implement IList and restrict access to the internal collection - personally I would write my own collection separate from Unit if this kind of type safety was a major concern).


Lastly, if you don't actually care about having a single-Creature-type Unit restriction, then just remove the generics from your own classes and methods entirely:

- Unit.critters : List<Creature>
- Army.units : List<Unit>

Thank you! This is exactly what I was looking for. I tested both of these and they both work.

I wanted to comment about an advantageous distinction between the first technique using generics vs. the second technique you describe where the type is used to filter out creatures...

Generics are a bit more visually messy and it's nice to be able to be avoid them if possible. That makes the second option you provide very attractive. There's a problem with that though: What if I want to spawn a bunch of creatures of some given type? (this is where my ignorance on C# language features may show up)

In a spawn method, I want to say, "Spawn X number of creatures of Type Y"
If I'm just given a class type, I don't know how to instantiate an object of that type. Here's my stab at a quick implementation of option #2:


public class Unit2
{
	public readonly Type m_type;

	List<Creature> critters = new List<Creature>();

	public Unit2(Type type)
	{
		m_type = type;
	}

	public void Spawn(int count)
	{
		for (int a = 0; a < count; a++)
		{
			Creature c = new Creature();
			object newCreatureType = Convert.ChangeType(c, m_type); //<-- Error: Object must implement IConvertible.
			critters.Add((Creature)newCreatureType);
		}
	}

	public void Add(Creature creature)
	{
		if (creature.GetType() != m_type)
		{
			Console.Write("Error: unit is not of right type!");
		}
		else
		{
			critters.Add(creature);
		}
	}
}

I really don't want to implement an IConvertible interface.

Implementing generics seems a lot easier (see below). The class type of the creature is included in the unit at run time, so I can instantiate an object of type T and it will be a creature of some derived class (goblin, troll, etc), and I don't have to know or care about what derived class it is.


public abstract class Unit
{
	public virtual void print()
	{
		
	}
}

public class Unit<T> : Unit where T : Creature, new()
{
	List<T> critters = new List<T>();

	public void Add(T item)
	{
		critters.Add(item);
	}

	public void Spawn(int count)
	{
		for (int a = 0; a < count; a++)
		{
			T creature = new T();
			critters.Add(creature);
		}
	}

	public override void print()
	{
		foreach (Creature c in critters)
		{
			c.print();
		}
	}
}
Hmm, having the Unit itself create more creatures seems odd, but without knowing anything about how your codebase is structured, here's one thing you can do (factory delegate).


public class Unit
{
    public readonly Type m_type;
    
    List<Creature> critters = new List<Creature>();
    
    Func<Creature> m_creationDelegate;

    public Unit(Type type, Func<Creature> creationDelegate)
    {
        m_type = type;
        m_creationDelegate = creationDelegate;
    }

    public void Spawn(int count)
    {
        for (int i=0; i<count; ++i)
            critters.Add(m_creationDelegate()); // invokes the delegate which returns a Creature.
    }
}

// ..... elsewhere

void ExampleUsageFunction()
{
    Unit goblinUnit = new Unit(typeof(Goblin), () => new Goblin()); // This lambda expression just creates a default Goblin.
}
This allows maximum flexibility - you can use anything which returns a Creature. If you need to, you can modify the function signature to allow the Unit to pass creation parameters to the creation delegate:



public class Unit
{
    public readonly Type m_type;
    
    public List<Creature> critters = new List<Creature>();
    
    Func<Unit,Creature> m_creationDelegate;

    public Unit(Type type, Func<Unit,Creature> creationDelegate)
    {
        m_type = type;
        m_creationDelegate = creationDelegate;
    }

    public void Spawn(int count)
    {
        for (int i=0; i<count; ++i)
            critters.Add(m_creationDelegate(this)); // invokes the delegate which returns a Creature.
    }
}

// ..... elsewhere

Unit MakeGoblinUnitWithLeader()
{
    return new Unit(typeof(Goblin), unit =>
    {
        var goblin = new Goblin();
        if (unit.critters.Count == 0) // this is the first Goblin being spawned in the Unit
            goblin.IsLeader = true; // The first Goblin being spawned in a Unit is the Leader (I'm just inventing a contrived example here...)
        return goblin;
    });
}

...

Okay, I really like this technique better than generics. It's so much cleaner and straight forward and doesn't require creating abstract method signatures in the base Unit class. Thanks!

Okay, I really like this technique better than generics.


eh??? Pretty sure that code still uses generics.


public class Unit
{
    public readonly Type m_type;
    
    public List<Creature> critters = new List<Creature>(); // Generics!
    
    Func<Unit,Creature> m_creationDelegate; // also generic

    public Unit(Type type, Func<Unit,Creature> creationDelegate) // even MOAR generics
    {
        m_type = type;
        m_creationDelegate = creationDelegate;
    }

    public void Spawn(int count)
    {
        for (int i=0; i<count; ++i)
            critters.Add(m_creationDelegate(this)); // invokes the delegate which returns a Creature.
    }
}


I'm not sure you've grasped what generics are in C#.

Don't take this the wrong way, but TBH, this whole concept is a bit messy.

Why do you need to know the Type of the creature? Be very wary of code that uses reflection. It's significantly slower, harder to debug and harder to navigate (for a start, visual studio won't tell all the places the code is used, etc)

Are you going to do some kind of


(if unit.m_type == typeof(Goblin)) // do goblin stuff

That's a huge code smell.


In my actual game though, the player wizard is an object which derives from the creature class. The player wizard can do all sorts of things which a standard creature doesn't do (like channel mana and cast spells) and has a whole bunch of attributes which no other creature needs to worry about (age, ghost state, etc). So, it would make sense for the wizard to be a derived class from Creature.

Actually it doesn't. It would be much better to use composition instead of inheritance here. You would probably be better off looking at an entity system

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight



public class Unit
{
    public readonly Type m_type;
    
    public List<Creature> critters = new List<Creature>(); // Generics!
    
    Func<Unit,Creature> m_creationDelegate; // also generic

    public Unit(Type type, Func<Unit,Creature> creationDelegate) // even MOAR generics
    {
        m_type = type;
        m_creationDelegate = creationDelegate;
    }

    public void Spawn(int count)
    {
        for (int i=0; i<count; ++i)
            critters.Add(m_creationDelegate(this)); // invokes the delegate which returns a Creature.
    }
}

I'm not sure you've grasped what generics are in C#.


In this thread, the phrase "using generics" currently means "defining our own generic classes and methods", not "using existing classes which are generic". Nobody's going to stop using generics entirely. That would be stupid.

Sorry for the confusion.

Don't take this the wrong way, but TBH, this whole concept is a bit messy.


I'm just working with what I've got. I don't time to refactor his entire project so I'm just showing him ways to solve his immediate goals with the minimum amount of redesign.

In the game, a unit is a collection of a specific type of creatures. So, I could have a unit of 25 archers, a unit of 40 swordsmen, a unit of 10 peasants, etc. I don't want to be able to mix and match creature types in a unit, so a unit shouldn't have 15 archers and 10 peasants. An army is a collection of units, so if you want to mix and match creatures, you create various unit types and add them to the army (one unit of 15 archers, another unit of 10 peasants).


Be wary of trying to model real-world concepts in code. OOP is _not_ meant to model how a human sees objects, but rather how the computer interacts with them.

Fundamentally, what is a Unit _from the computer's perspective_ ? What is a Creature? Do they need to be separate? Could you install model everything with a Unit that contains a single Creature instance and then a list of individual creature instances (position/current hp/etc.) ? Something like:

class Creature {};
struct Instance {
  vec4 position;
  int currentHits;
};
class Unit {
  Creature m_Type;
  List<Instance> m_Instances;

  public Unit(Creature type) { m_Type = type; }

  // add another instance to the unit
  public AddInstance(vec4 position) {
   Instance inst;
   inst.position = position;
   inst.currentHits = m_Type.MaxHitPoints;
   m_Instances.Add(inst);
  }
};

Sean Middleditch – Game Systems Engineer – Join my team!

@ChaosEngine:

I'm just going off of the MSDN definition for generics in C#, which would be something like "class Unit<T>", where the T is the template / "generic" term..

The sections you highlighted are base classes which take advantage of polymorphism... which I think is different from generics.

I'd use a component-entity system if I had a lot of time. I implemented one a while back and I found that it has a lot of programmer overhead (but maybe it was due to poor design?). As for getting something up fast and working as a prototype, the OOP hierarchy is sufficient enough.

@SeanMiddleditch:
A unit is mostly just a manager class for a group of creatures (similar to TotalWar). I don't let players interact with individual creatures, they have to interact with units. They can select a unit and give it a movement order which contains position and orientation information. The creatures themselves are quite dumb, but the unit will tell them where in a formation they should stand. As for my object heirarchy, it looks something like this:

GenericGameObject <-- Physical <-- Actor <-- Creature <-- Humanoid <-- Human <-- [Instances]

I probably went a bit overboard with that, but the computer probably doesn't care. As far as the computer can tell, it's just a bunch of data in the memory banks. IMHO, it's more important for stuff to make conceptual sense to me than it is for the computer.

This topic is closed to new replies.

Advertisement