This is quickly becoming a thread where I talk to myself but I think a light bulb just went on. I guess the Dictionary class in C# would actually be equal to the array of pointers in C++, not the container class that holds them. What I assume I need to do is create a container class of my own that includes a dictionary class within it. The dictionary class is used to store the objects and the container class will add/remove/modify those objects in response to requests from the outside world.
Which just leaves the question of whether the Dictionary class can handle late-binding
Steve
I'm going to selectively answer tidbits from the various posts. I'm hitting this one first, since I think it's got a lot of the most important kernels.
1) You're right, in that Dictionary (and other containers) should be used as concrete classes that you include in other objects, rather than as base classes from which you derive.
2) If you want something that's close to an array of objects, then List<T> is the container you want. This is a simple container that can be iterated through using a foreach block, and allows multiple inclusions of the same object. Dictionary<TKey, TValue> is what I usually hear referred to as an "associative array" - it allows keyed access. Note that (IIRC) a Dictionary is implemented as something close to a hash table, in other words you can find an element in it without doing an O(N) search. Also note that the key values in a dictionary need to be unique.
3) It is important in C# to distinguish between Interfaces and Abstract Base Classes. Generally speaking, you want to do polymorphism (the moral equivalent of virtual methods) by implementing interfaces, not by deriving from an abstract base class. So for your MapObject example, you would want to define an interface IMapObject, which Ship, or Planet or Missile classes all implement. In other words, interface inheritance (defining a set of methods whose implementations are bound at run-time based on the concrete type of the particular instance) is supported most naturally in the language by implementing interfaces, NOT by deriving from Abstract base classes. Abstract base classes, on the other hand, are most useful to provide default implementations for the methods in the interface, i.e. it is most usefull for performing implementation inheritance (where different derived classes reuse common base class code), while still maintaining polymorphism for unshared functionality through virtual methods.
In C++ language, you won't go far wrong if you think of an Interface as being the same as a C++ Abstract Base Class with no data members and only pure virtual methods. This also makes sense in terms of multiple inheritance - a C# class can only derive from a single class, but it can inherit from as many Interfaces as you want. In C++, multiple inheritance doesn't stress the compiler if the "extra" base classes are all have no data or non-pure-virtuals.
Another good principle in all languages is "virtual methods should never be public". The reason for this is that a virtual method is an interface for people who are going to derive from (specialize) the class, while public methods are an interface for people who are going to use the class (by calling its methods). A public virtual method is trying to take on both responsibilities simultaneously - usually a bad idea :-) Generally speaking, a virtual method should be protected (in C# - in C++ it can actually be made private), with a corresponding public non-virtual method that calls it, e.g.
public int Foo() {return FooImpl();}
protected virtual int FooImpl();
Putting all this together a nice pattern comes out: For every interface e.g. IMapObject, define a corresponding "abstract" base class (which doesn't actually have to have any abstract or virtual methods) which implements the interfaces methods and which concrete classes can (but are not forced to) use as a base class. For example:
public class Point3D
{
// actually this is a class in System.Windows.Media.Media3D
}
public interface IMapObject
{
Point3D Location { get; set; }
}
public class AMapObject : IMapObject
{
#region private data
Point3D _location;
#endregion
public Point3D Location
{
get
{
return _location;
}
set
{
_location = value;
}
}
}
public class Planet : AMapObject
{
#region private data
double _radius;
#endregion
public double Radius
{
get { return _radius; }
set { _radius = value; }
}
}
public interface IWeaponsPlatform
{
void Shoot();
}
public abstract class AWeaponsPlatform : IWeaponsPlatform
{
#region virtual implementations of public methods
protected abstract void ShootImpl();
#endregion
public void Shoot()
{
ShootImpl();
}
}
public class Ship : AWeaponsPlatform, IMapObject
{
#region private data
AMapObject _mapObjectHelper; // utilize services of AMapObject by aggregation rather than inheritance
#endregion
public Point3D Location
{
get
{
return _mapObjectHelper.Location;
}
set
{
_mapObjectHelper.Location = value;
}
}
protected override void ShootImpl()
{
throw new NotImplementedException();
}
}
public class MapObjectManager // class to demo lists....
{
#region private data
List<IMapObject> _mapObjects = new List<IMapObject>();
#endregion
public int Add(IMapObject newObject)
{
_mapObjects.Add(newObject);
return _mapObjects.Count;
}
public List<IMapObject> Objects
{
get
{
return _mapObjects;
}
}
public static void ExampleMethod() // intended to demonstrate use of a list
{
MapObjectManager mgr = new MapObjectManager();
int firstCount = mgr.Add(new Planet()); // firstCount should == 1
int secondCount = mgr.Add(new Ship()); // secondCount should == 2
foreach (IMapObject obj in mgr.Objects)
{
Point3D thisLocation = obj.Location;
// do something with thisLocation
}
}
}
(Ok - I got kind of carried away with the example code, but it's just so easy to rattle off with the IDE :-) )
Any of your concrete map objects, e.g. planet, missile, ship, can derive from AMapObject (which you'll notice doesn't actually have any abstract or virtual methods), or if they need to derive from something else that's more useful, they can still implement the IMapObject interface. This is demonstrated above by Planet (which derives from AMapObject) and Ship, which derives from the presumably more useful AWeaponsPlatform object. The MapObjectManager class is intended to give examples of how to create a list of IMapObject objects and then use them in a foreach loop.
BTW, in the IDE, the "tab" key is your friend - most times you only need to hit about 2 keystrokes before you can tab and get auto-completion. You can also right-click on the stuff after the ":" e.g. IMapObject and the IDE will fill in stubbed out routines which implement the interface or abstract class.
I think that this is enough for this one - going to go answer some other posts now....
John