Aurora 4x

Off Topic => Off Topic => Topic started by: Steve Walmsley on October 11, 2010, 10:35:45 PM

Title: Programming Advice
Post by: Steve Walmsley on October 11, 2010, 10:35:45 PM
On VB2010, Joel On Software (a collection of blogs from a very insightful guy) characterizes the various .NET languages as being different syntactic sugar (my name) on top of the same underlying language.  This is accurate - what they've done is added language extensions to the various languages to essentially let you write C# (which is a truly excellent language) in those languages.  If you go to a .NET language, you might be better off simply biting the bullet and learning C# - after all, didn't SA start out as your toy to learn VB? :-)
I took your advice and looked at Visual C#. I think that is definitely the way to go so I have been following the Microsoft tutorials and written my first few test programs. The OO side is very familiar in terms of concepts, although its been a while :). Some very useful features on the UI as well, especially the TableLayoutPanel. I don't think resolution will be an issue for my next project as its very easy to write windows that scale with size.

I could use some advice though on one particular decision. I am creating a new program for me to play with Newtonian combat models and I need to create a collection class to hold ships. In C++ I created my own container classes and in VB6 there is simply one Collection object. However, in C# there is a whole array (pardon the pun!) of different Collection objects from which to derive my Ship collection, and other similar collections I will need in the future. I am guessing that I should use the Dictionary class but this is a fundamental design question so I need to get it right. Any suggestions?

Steve
Title: Re: Programming Advice
Post by: Steve Walmsley on October 12, 2010, 05:54:38 AM
Some more info on what I am trying to achieve. Bear in mind my OO knowledge is more than 10 years out of date so I may be using obsolete terms. I have an abstract base class called MapObject that I intend to use as the basis for a hierarchy of different objects that will appear on the eventual map for this game, including stars, planets, ships, missiles, etc.. Ship would be a derived class, or perhaps PoweredObject would be derived directly from MapObject and Ship would be derived from PoweredObject, along with Missile. Anyway, in C++ terms what I need to do is create a container class with an array of base class pointers so I can point them to derived objects of MapObject without knowing at design time what those objects will be (Late Binding I think it used to be called).

In VB6 there is a collection object which is based on arrays of a specific class, but VB6 doesn't support inheritance so there is no issue with derived classes although it obviously lacks flexibility.

In C# terms, I think I need a Dictionary collection to serve as my container class, which can apparently accept paired keys and values at design time. As I understand it, with my two days of experience :), the values can be simple data types or user-defined objects while the keys can be numeric or string-based. The Dictionary object apparently has strong type safety (where is the fun in that!) so does that mean I can only add one type of object, or can I specify MapObject as the value and then add any derived class? Also, to add my own functions at the container class level, do I actually need to create my own derived class based on Dictionary with additional, or overloaded, functions.

My intention is to have the tracking of an object's location, its speed and course, etc. and the various trigonometry functions in MapObject and then add functionality and additional data as appropriate to derived classes. For example, PoweredObject would have the thrust and fuel data and the functions for modifying course and speed based on applied thrust and changes in mass. Missile would have warhead information, tracking functions, etc. Ship would obviously have much more information and a lot of new functions. This will only work however if I can run through a for each loop and move everything at once on the Map as time progresses, which is why I need to figure out if Dictionary supports late binding or I need a different collection class, or I need to create my own collection class.

Any help appreciated. I will continue to try and work this out by myself as well :)

Steve
Title: Re: Programming Advice
Post by: Steve Walmsley on October 12, 2010, 07:20:22 AM
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
Title: Re: Programming Advice
Post by: Beersatron on October 12, 2010, 08:08:22 AM
Steve, I have never done C# so can't help you there but do you mind sharing your development setup? I might have a bash at something, I need to diversify a bit and so far it is just Coldfusion by Adobe that I do with a setup on my laptop to play around with AIR - also by Adobe.
Title: Re: Programming Advice
Post by: ndkid on October 12, 2010, 10:21:45 AM
In C# terms, I think I need a Dictionary collection to serve as my container class, which can apparently accept paired keys and values at design time.

The dictionary collection is somewhat more powerful than you give it credit for with respect to the key. It is not limited to numeric or string types... you can pretty much make any sort of object your key, and can override how keys are compared to each other to determine equality.

Whether a dictionary is the best choice for your work is mostly a matter of a few factors: access, addition, and deletion. If you plan to add and remove Map Objects arbitrarily, and also want to be able to access any given map object quickly, then a Dictionary is probably a good call. If you're willing to place a cap on the number of map objects in existence, and can index with just an int, then a plain old array gives you even faster performance than a dictionary. There's probably no reason to use a list in this case, since there's no linear relationship among map objects.

The Dictionary object apparently has strong type safety (where is the fun in that!) so does that mean I can only add one type of object, or can I specify MapObject as the value and then add any derived class? Also, to add my own functions at the container class level, do I actually need to create my own derived class based on Dictionary with additional, or overloaded, functions.

It does mean that you can only add one type of object, but that's not really meaningful in more fully OO languages like C#, because everything is an object and, therefore, you can ultimately make a collection of type object and stick whatever you want into it. In fact, because C# has reflection, you could write some really godawful code like:

foreach(object foo in objects)
{
   if(foo.GetType() == typeof(int))
   {
       return 1;
   }
   else if(foo.GetType() == typeof(bar))
   {
      bar bar1 = foo;
      bar1.someFunction();
   }
}


This would be the sort of code, mind you, that would make your average OO programmer rather distressed! :-) Instead, a more OO solution would be pretty much what you propose: having a base object that things inherit from and using polymorphism. The trick is ensuring that you design your objects in a way that most things meant to deal with MapObjects don't have a reason to try and get to any subclass-specific methods or properties. So, for example, your MapObjectCollection should not itself care what sort of map object it has in its internal collection (whether it turns out to be a dictionary or something else); it should just hand out MapObject objects and, if it interacts with them, only does so as MapObjects; it shouldn't take one, figure out whether it's a missile, and then run missile-specific code for it.

If you ever want more rapid responses to C# questions, you can PM me, and I'll send along my email address so you can contact me directly. Sadly, I can't point you at any guys local to you, as my company's closest office is Belfast. :-)
Title: Re: Programming Advice
Post by: ndkid on October 12, 2010, 10:32:06 AM
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.

In C#, all objects are accessed by reference. So, in essence, whenever you declare
Foo foo;
you've only made a pointer, which is why objects in C# generally need to be constructed:
Foo foo = new Foo();

So, a dictionary certainly contains what in C++ would be called pointers, but it's most definitely not an array... C# still has array notation. So an array of pointers in C# could be:
Foo[] foo = new Foo[20];

Dictionaries are slightly more complicated, because they're based on hash tables. If you're interested in data structure design, I can go into deeper detail, but, basically, the John Q. Average Dictionary design is usually based on an expanding array that is at least twice as big as the expected size of the Dictionary. The main reason Dictionaries exist is when people want the rapid access that arrays have (and lists don't) but want more dynamic addition/deletion than arrays provide.

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.

Whether or not you need to wrap the dictionary within another class depends on how you want the collection of objects to be accessed. If the wrapping class just exposes the ability to access and modify members of the collection, you haven't really added much. In essence, the only reason I can think of off the top of my head to wrap the MapObject collection is if you don't want anything outside it to directly use MapObject.
Title: Re: Programming Advice
Post by: Steve Walmsley on October 12, 2010, 05:12:31 PM
Steve, I have never done C# so can't help you there but do you mind sharing your development setup? I might have a bash at something, I need to diversify a bit and so far it is just Coldfusion by Adobe that I do with a setup on my laptop to play around with AIR - also by Adobe.

Not sure what you mean by my development setup. I am just playing with Visual C# 2010 at the moment. I have done the simple Microsoft tutorials and I am reading through various sections on the MSDN as I try out different things. It is a hit and miss approach but I like to tinker :). I have also ordered a book called Microsoft Visual C# 2010 Step by Step by John Sharp as it had good reviews on Amazon. I am going to use Access initially, although after advice from ndkid I am going to try and make sure my data access is DB independent so I could swap to SQL Server at some point.

Steve
Title: Re: Programming Advice
Post by: Steve Walmsley on October 12, 2010, 05:26:47 PM
The dictionary collection is somewhat more powerful than you give it credit for with respect to the key. It is not limited to numeric or string types... you can pretty much make any sort of object your key, and can override how keys are compared to each other to determine equality.

Whether a dictionary is the best choice for your work is mostly a matter of a few factors: access, addition, and deletion. If you plan to add and remove Map Objects arbitrarily, and also want to be able to access any given map object quickly, then a Dictionary is probably a good call. If you're willing to place a cap on the number of map objects in existence, and can index with just an int, then a plain old array gives you even faster performance than a dictionary. There's probably no reason to use a list in this case, since there's no linear relationship among map objects.

It does mean that you can only add one type of object, but that's not really meaningful in more fully OO languages like C#, because everything is an object and, therefore, you can ultimately make a collection of type object and stick whatever you want into it. In fact, because C# has reflection, you could write some really godawful code like:

foreach(object foo in objects)
{
   if(foo.GetType() == typeof(int))
   {
       return 1;
   }
   else if(foo.GetType() == typeof(bar))
   {
      bar bar1 = foo;
      bar1.someFunction();
   }
}


This would be the sort of code, mind you, that would make your average OO programmer rather distressed! :-) Instead, a more OO solution would be pretty much what you propose: having a base object that things inherit from and using polymorphism. The trick is ensuring that you design your objects in a way that most things meant to deal with MapObjects don't have a reason to try and get to any subclass-specific methods or properties. So, for example, your MapObjectCollection should not itself care what sort of map object it has in its internal collection (whether it turns out to be a dictionary or something else); it should just hand out MapObject objects and, if it interacts with them, only does so as MapObjects; it shouldn't take one, figure out whether it's a missile, and then run missile-specific code for it.

If you ever want more rapid responses to C# questions, you can PM me, and I'll send along my email address so you can contact me directly. Sadly, I can't point you at any guys local to you, as my company's closest office is Belfast. :-)
Thanks for the response and the offer!

My hope was that I could setup Dictionary to add MapObjects, fill it with derived objects and then override various base class functions to ensure that the container class would call functions appropriate to the complexity of the derived object. For example, if I create an UpdateCourseSpeed function, which will handle changes to course and speed based on changes in thrust, facing and external forces such a gravity, that would require code for missiles, more complex code for ships and different code for something like an asteroid, which would perhaps be a SystemBody object, derived from MapObject. The base UpdateCourseSpeed function would be in MapObject. Missile, Ship and SystemBody would have their own version of the same function. I would like to be able to run a single for each loop for every MapObject and have the code take the appropriate action for each object, although using the overriden function rather than checking the type of each object first. This is straighforward in C++. Is that what you mean above for C#, or will I need a separate Dictionary class to hold collections of Missile, Ship and SystemBody objects? Or am I just way out of date in terms of thinking and there is a better way to handle this?

Steve
Title: Re: Programming Advice
Post by: Steve Walmsley on October 12, 2010, 05:31:56 PM
Whether or not you need to wrap the dictionary within another class depends on how you want the collection of objects to be accessed. If the wrapping class just exposes the ability to access and modify members of the collection, you haven't really added much. In essence, the only reason I can think of off the top of my head to wrap the MapObject collection is if you don't want anything outside it to directly use MapObject.

I want to be able to run through the objects and perform various functions on them, or select a particular object by key and modify it. Can I add my own functions to the Dictionary class, such as the UpdateCourseSpeed I mentioned in my previous answer? I suppose what I am asking is should I use inherit my own version of Diictionary class with additional functions to manipulate the MapObjects, or should I just use Dictionary to hold the objects and wrap a class around that Dictionary with the functions I need? Or I am totally misunderstanding the Dictionary class?

Steve
Title: Re: Programming Advice
Post by: Beersatron on October 12, 2010, 08:47:35 PM
Not sure what you mean by my development setup. I am just playing with Visual C# 2010 at the moment. I have done the simple Microsoft tutorials and I am reading through various sections on the MSDN as I try out different things. It is a hit and miss approach but I like to tinker :). I have also ordered a book called Microsoft Visual C# 2010 Step by Step by John Sharp as it had good reviews on Amazon. I am going to use Access initially, although after advice from ndkid I am going to try and make sure my data access is DB independent so I could swap to SQL Server at some point.

Steve

What editor do you use, does it debug and compile and step-through etc? Is there anything else you need installed other than an editor and the .net framework? Guess I am just used to developing web applications which require 3 or 4 different pieces of software just to get your server up and running correctly!
Title: Re: Programming Advice
Post by: Erik L on October 12, 2010, 09:59:34 PM
What editor do you use, does it debug and compile and step-through etc? Is there anything else you need installed other than an editor and the .net framework? Guess I am just used to developing web applications which require 3 or 4 different pieces of software just to get your server up and running correctly!
Visual Studio (VS) has the editor, compiler, step-over/into, etc all in it.

You'll need the .NET framework, and I'd suggest the various SDKs just because you never know what you might need. For databases, Access or SQLExpress are needed also.
Title: Re: Programming Advice
Post by: sloanjh on October 12, 2010, 10:42:20 PM
Visual Studio (VS) has the editor, compiler, step-over/into, etc all in it.

You'll need the .NET framework, and I'd suggest the various SDKs just because you never know what you might need. For databases, Access or SQLExpress are needed also.
In particular, you want VS (probably 2010) Express - the free version.

John
Title: Re: Programming Advice
Post by: Steve Walmsley on October 13, 2010, 12:01:18 AM
OK I have hit my first serious hitch and I don't have a clue what the problem is :)

This is a function which I am going to use to load Ship objects into a collection. At the moment, the assignment to a collection isn't in the code but it will be within the forearch loop. The problem is that the code never reaches the foreach loop. I have no runtime errors and no compilation errors. I do have breakpoints on the statement above the foreach loop and on the foreach loop itself. The breakpoint is triggered for the statement above but the foreach breakpoint is never triggered. For some reason the code doesn't get there, although the form that is calling this function loads without any problem. Any suggestions?

    // container class for MapObjects
    public class MapObjectContainer
    {

        // create an instance of the Dictionary class to hold all the MapObjects
        Dictionary<int, MapObject> mCol = new Dictionary<int, MapObject>();
       
        // default constructor
        public MapObjectContainer()
        {
        }
       
         // function to load ships from database
        public void LoadDBShips()
        {

            // create an instance of the table adaptor for the Ship table
            DamoclesDataSetTableAdapters.ShipTableAdapter ShipTable = new DamoclesDataSetTableAdapters.ShipTableAdapter();

            // this code creates a new data table by calling the get data function of the table adaptor, with a parameter for GameID
            DamoclesDataSet.ShipDataTable ShipData = ShipTable.GetDataByGame(1);

            // cycle through rows in data table and use them to create objects
            foreach (DamoclesDataSet.ShipRow ShipRow in ShipData)
            {
                Ship objShip = new Ship();
                objShip.ShipID = ShipRow.ShipID;
                objShip.ShipName = ShipRow.ShipName;
                objShip.PoliticalID = ShipRow.PoliticalID;
                objShip.ShipClassID = ShipRow.ShipClassID;
                objShip.Mass = ShipRow.Mass;
                objShip.Xcor = ShipRow.XCor;
                objShip.SystemID = ShipRow.SystemID;
                objShip.Ycor = ShipRow.YCor;
               
            }
        }
    }

Steve
Title: Re: Programming Advice
Post by: sloanjh on October 13, 2010, 12:17:39 AM

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.
Code: [Select]
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:

Code: [Select]
    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
Title: Re: Programming Advice
Post by: sloanjh on October 13, 2010, 12:25:35 AM
This is a function which I am going to use to load Ship objects into a collection. At the moment, the assignment to a collection isn't in the code but it will be within the forearch loop. The problem is that the code never reaches the foreach loop. I have no runtime errors and no compilation errors. I do have breakpoints on the statement above the foreach loop and on the foreach loop itself. The breakpoint is triggered for the statement above but the foreach breakpoint is never triggered. For some reason the code doesn't get there, although the form that is calling this function loads without any problem. Any suggestions?

Are you sure it's not throwing in the GetDataByGame(1) call?  Note that by default Visual Studio doesn't turn on most exceptions - you have to go into Debug/Exceptions (or something like that) and click the appropriate checkbox.

The only other thing I can think of is that there aren't any ShipRows in ShipData, but that doesn't make a lot of sense because my experience is that VS-for-C# is a lot more anal about stepping through the various evaluations in a foreach statement - you can sit on the foreach line for 2-3 pushes of F10.

What happens if you go to your breakpoint and then start hitting F10?  If it exits, then I suspect that you've got a throw that VS isn't stopping on (like I said a paragraph or two back).

John
Title: Re: Programming Advice
Post by: Steve Walmsley on October 13, 2010, 12:29:46 AM
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.

As well as looping through the list I also need to be able to access a specific item based on its key, such as ShipID. I was going to ensure that all objects have a unique identifier wthin the game, not just within a specific type of object. Does that type of keyed access mean the Dictionary is still the way to go?

Steve
Title: Re: Programming Advice
Post by: sloanjh on October 13, 2010, 12:32:34 AM
My hope was that I could setup Dictionary to add MapObjects, fill it with derived objects and then override various base class functions to ensure that the container class would call functions appropriate to the complexity of the derived object. For example, if I create an UpdateCourseSpeed function, which will handle changes to course and speed based on changes in thrust, facing and external forces such a gravity, that would require code for missiles, more complex code for ships and different code for something like an asteroid, which would perhaps be a SystemBody object, derived from MapObject. The base UpdateCourseSpeed function would be in MapObject. Missile, Ship and SystemBody would have their own version of the same function. I would like to be able to run a single for each loop for every MapObject and have the code take the appropriate action for each object, although using the overriden function rather than checking the type of each object first. This is straighforward in C++. Is that what you mean above for C#, or will I need a separate Dictionary class to hold collections of Missile, Ship and SystemBody objects? Or am I just way out of date in terms of thinking and there is a better way to handle this?

I think my big post discussed this.  The bottom line is that you want your collection to be of IMapObjects (interfaces which the concrete objects implement) - the code in the loop calls the IMapObject methods, while the concrete type gives the polymorphism.  Let me know if you want more example code - I think what I put in probably addresses what you're after....

John
Title: Re: Programming Advice
Post by: sloanjh on October 13, 2010, 12:33:39 AM
As well as looping through the list I also need to be able to access a specific item based on its key, such as ShipID. I was going to ensure that all objects have a unique identifier wthin the game, not just within a specific type of object. Does that type of keyed access mean the Dictionary is still the way to go?
Yep.  If you want keyed access, then Dictionary is a better choice.
Title: Re: Programming Advice
Post by: Steve Walmsley on October 13, 2010, 12:35:16 AM
Are you sure it's not throwing in the GetDataByGame(1) call?  Note that by default Visual Studio doesn't turn on most exceptions - you have to go into Debug/Exceptions (or something like that) and click the appropriate checkbox.

Yes, that is the problem. There is a runtime error on that line once I click the Thrown checkboxes. It can't find the database file at runtime for some reason, even though it can see it during design. I'll try and figure out what the new problem is - thanks!

Steve
Title: Re: Programming Advice
Post by: Steve Walmsley on October 13, 2010, 12:37:04 AM
I think my big post discussed this.  The bottom line is that you want your collection to be of IMapObjects (interfaces which the concrete objects implement) - the code in the loop calls the IMapObject methods, while the concrete type gives the polymorphism.  Let me know if you want more example code - I think what I put in probably addresses what you're after....

I need to start reading up on interfaces as this is something I didn't use much (or have forgotten about) and then go through your code properly. It will probably be tomorrow before I can reply intelligently :)

Steve
Title: Re: Programming Advice
Post by: sloanjh on October 13, 2010, 12:42:27 AM
Yes, that is the problem. There is a runtime error on that line once I click the Thrown checkboxes. It can't find the database file at runtime for some reason, even though it can see it during design. I'll try and figure out what the new problem is - thanks!

NP.  The amusing thing is that I've been contemplating the project you're doing (3D Newtonian Space War) as a toy project to play with C# at home, just to be doing something.  Another possibility was to start to code up the Harpoon rules (which is how I learned C 25 years ago or so).

Before you go too far down your route, you should take a look at the LINQ stuff - it basically lets you write SQL syntax rather than C++ syntax for filters etc.  Since your code is usually so DB-centric, you might find it simplifies your life.  Also, the F1 key is very useful - if you put your cursor on any code element and hit F1, it takes you to MSDN help for that code element, which includes sample code in the various .NET languages.  Like I said elsewhere - they really did a good job with the language and programming environment.

Bedtime for me :-)

John
Title: Re: Programming Advice
Post by: sloanjh on October 13, 2010, 12:47:39 AM
I need to start reading up on interfaces as this is something I didn't use much (or have forgotten about) and then go through your code properly. It will probably be tomorrow before I can reply intelligently :)

They basically define a contract (set of services) that any class that implements the interface is required to provide.  That means that any object which implements the contract can be passed to a function that only uses the contract.  The analogue in C++ is abstract base classes in a multiple-inheritance environment.

They also participate in generics (similar to C++ templates) - when specifying a template argument, you can say "it must implement interface IFoo", then the template code can utilize the methods of IFoo.  You've probably already figured out by now that Dictionary<TKey, TObject> is doubly generic over key and object types.

Have fun mulling :-)

John
Title: Re: Programming Advice
Post by: Steve Walmsley on October 13, 2010, 01:02:04 AM
Based on my brief read up on interfaces, my own interpretation is that they are a template for the function calls that an object should be able to handle, even if they handle the implementation of those function calls differently. So as long as you know a particular class has the correct interface, you can call those functions and not worry about it. As a single class can handle multiple templates, it could potentially be used within a variety of different collections that have different purposes as long as all the classes assigned to the same collection have the same interface, which effectively creates a many to many relationship between classes and collections as long as you implement the relevant interfaces correctly. Also, if you inherit a template within a base class, you can implement the same interface differently within derived classes, which as you say has the same effect as virtual functions.

Was that understandable or did I just talk a lot of rubbish? :)

Steve
Title: Re: Programming Advice
Post by: sloanjh on October 13, 2010, 01:37:04 AM
Based on my brief read up on interfaces, my own interpretation is that they are a template for the function calls that an object should be able to handle, even if they handle the implementation of those function calls differently. So as long as you know a particular class has the correct interface, you can call those functions and not worry about it. As a single class can handle multiple templates, it could potentially be used within a variety of different collections that have different purposes as long as all the classes assigned to the same collection have the same interface, which effectively creates a many to many relationship between classes and collections as long as you implement the relevant interfaces correctly. Also, if you inherit a template within a base class, you can implement the same interface differently within derived classes, which as you say has the same effect as virtual functions.

Was that understandable or did I just talk a lot of rubbish? :)
Yep - that's pretty much it.  Your "template" is what I called "contract".  A slight difference at the end - if a base class implements an interface, then you have to (IIRC) provide all the methods of the interface in the base class.  You can however (using the abstract or virtual Impl trick I discussed) put a polymorphic implementation behind those methods.


Ok - really logging off now :-)

John
Title: Re: Programming Advice
Post by: Steve Walmsley on October 13, 2010, 09:57:45 AM
Next problem :)

When I originally setup the data source, the wizard asked if I wanted to include the database in the program files. I said no, as I wasn't really sure what that would entail. I placed the mbd file in the project directory instead from where I edit it using Access. The IDE can see the database because it can retrieve both structure and data. However, when the program runs it throws an error because for some reaon it is looking for the database file in /bin/debug. So 2 questions:

1) Should I be including the database file in the program files, and if so how do I go back and change that?

2) If not, how do get the IDE and the program to look in the same place for the DB file?

Steve
Title: Re: Programming Advice
Post by: ndkid on October 13, 2010, 10:01:10 AM
I want to be able to run through the objects and perform various functions on them, or select a particular object by key and modify it. Can I add my own functions to the Dictionary class, such as the UpdateCourseSpeed I mentioned in my previous answer? I suppose what I am asking is should I use inherit my own version of Diictionary class with additional functions to manipulate the MapObjects, or should I just use Dictionary to hold the objects and wrap a class around that Dictionary with the functions I need? Or I am totally misunderstanding the Dictionary class?

Yep, you can create a MapObjects collection (of any type, really, even an old style array), insert objects that inherit from MapObjects into it, and then call their functions and have the subclass-specific implementations get run properly.

Here's a not-quite-fully implemented example.

Code: [Select]
using System.Collections.Generic;

namespace MyNamespace
{
    public abstract class MapObject
    {
        public virtual void UpdateCourseSpeed(int newSpeed)
        {
            _speed = newSpeed;
        }

        protected int _speed;
    }

    public class Missile : MapObject
    {
        public override void UpdateCourseSpeed(int newSpeed)
        {
            _speed = newSpeed > maxSpeed ? maxSpeed : newSpeed;
        }

        private int maxSpeed = 100;
    }

    public class MapObjectHolder
    {
        private readonly Dictionary<int, MapObject> _mapObjectDictionary;

        public MapObjectHolder()
        {
            _mapObjectDictionary = new Dictionary<int, MapObject>();
        }

        public void AdjustSpeed(int mapObjectId, int newSpeed)
        {
            if(_mapObjectDictionary.ContainsKey(mapObjectId))
            {
                _mapObjectDictionary[mapObjectId].UpdateCourseSpeed(newSpeed);
            }
        }
    }
}
Title: Re: Programming Advice
Post by: ndkid on October 13, 2010, 10:14:57 AM
Next problem :)

When I originally setup the data source, the wizard asked if I wanted to include the database in the program files. I said no, as I wasn't really sure what that would entail. I placed the mbd file in the project directory instead from where I edit it using Access. The IDE can see the database because it can retrieve both structure and data. However, when the program runs it throws an error because for some reaon it is looking for the database file in /bin/debug. So 2 questions:

1) Should I be including the database file in the program files, and if so how do I go back and change that?

2) If not, how do get the IDE and the program to look in the same place for the DB file?

Steve
My first guess, not having a lot of details of how you set up your project: go to server explorer and add a reference to the location of the file. See: http://msdn.microsoft.com/en-us/library/bb384568.aspx
Title: Re: Programming Advice
Post by: Steve Walmsley on October 13, 2010, 10:31:51 AM
My first guess, not having a lot of details of how you set up your project: go to server explorer and add a reference to the location of the file. See: http://msdn.microsoft.com/en-us/library/bb384568.aspx

I can't find that option on the View menu. I am using Visual C# Express though so it may not be there. I have temporarily resolved the problem by placing a copy of the database file in the bin/debug directory. It also occurred to me to simply point the IDE at that location as well but I can find no way to do that and no way to just delete the data source and start again. Just unfamiliarity with the IDE I imagine but its very frustrating. So at the moment I have two copies of the database and I will have to remember to copy the master database to the copy database location when I make changes, although I suppose that creates an inherent backup if anything goes seriously wrong :)

Steve
Title: Re: Programming Advice
Post by: ndkid on October 13, 2010, 11:14:48 AM
I can't find that option on the View menu. I am using Visual C# Express though so it may not be there. I have temporarily resolved the problem by placing a copy of the database file in the bin/debug directory. It also occurred to me to simply point the IDE at that location as well but I can find no way to do that and no way to just delete the data source and start again. Just unfamiliarity with the IDE I imagine but its very frustrating. So at the moment I have two copies of the database and I will have to remember to copy the master database to the copy database location when I make changes, although I suppose that creates an inherent backup if anything goes seriously wrong :)

Alright, my second guess: the app.config (if you're generating foo.exe, it'll probably be called foo.exe.config) may have the db data. It may just have the file name, thus looking for it in the same location as the .exe/path. If I'm right, that's where you could change it.
Title: Re: Programming Advice
Post by: Steve Walmsley on October 13, 2010, 11:32:45 AM
Alright, my second guess: the app.config (if you're generating foo.exe, it'll probably be called foo.exe.config) may have the db data. It may just have the file name, thus looking for it in the same location as the .exe/path. If I'm right, that's where you could change it.

Found it, although it just has the DB set up as the root directory, which the program thinks is bin\debug. I assume that C# programs are setup by default to run in \ProjectDirectory\bin\debug and that is where it expects to find the DB. I could possibly have a DB in a sub-directory but not in a higher directory. What I really need to be able to do is change the datasource from the DB in \ProjectDirectory to the DB in the \ProjectDirectory\bin\debug directory so I don't end up maintaining two versions of the same DB. At the moment though I can't work out how to do that :)

Steve
Title: Re: Programming Advice
Post by: Steve Walmsley on October 13, 2010, 11:47:34 AM
OO design question, as I have been VB-corrupted over the last few years :)

On program start, all the ships are loaded into a collection within my MapObjectContainer class. I intended to also add all missiles, system bodies, etc to this same collection. However, I have a function to load the names of all the ships of a particular race into a listbox. Of course, my MapObject idea doesn't hold up so well now because I only need to use one value from some of the Ship objects (derived from MapObject). In Aurora this is simply loaded straight from the DB but my new program I intend to keep DB access to an absolute minimum for performance reasons. So if I cycle though the Directory class that is holding my MapObjects, including Ships, and try to use the following code:

            // loop through every map object
            foreach (KeyValuePair<int,MapObject> objShip in mCol)
            {
                // add a value from the object to the listbox
                lstShip.Items.Add(objShip.Value.ShipName);
            }

It doesn't work because the only directly accessible properties are those of the base class (currently Xcor, Ycor and SystemID). As the collections are reference-based, would it be acceptable OO practice to create multiple Directionaries within the container class. One Dictionary for Ships, one for Missiles, one for SystemBodies, etc. but also combined lists such as a Dictionary containing all MapObjects, or all PoweredObjects. In that way, I could perform operations on a specific subset of MapObjects when I needed to but also access them all in a single foreach loop when that was appropriate too. Or is there a more elegant way to achieve this?

EDIT: Here is the code for the above. It's working fine but I don't want to be an OO heretic :)

Code: [Select]
namespace Damocles
{

    public class MapObject
    {
        // data fields
        public Decimal Xcor;
        public Decimal Ycor;
        public int SystemID;

        // default constructor
        public MapObject()
        {
            Xcor = 0;
            Ycor = 0;
            SystemID = 0;
        }

        // constructor that accepts absolute starting location values
        public MapObject(Decimal X, Decimal Y, int SysID)
        {
            Xcor = X;
            Ycor = Y;
            SystemID = SysID;
        }
    
    }

    public class Ship : MapObject
    {

        // data fields
        public int ShipID;
        public int ShipClassID;
        public int PoliticalID;
        public string ShipName;
        public decimal Mass;

        // default constructor
        public Ship()
        {
        }

        // constructor
        public Ship(int intShipID, string strShipName, int intPoliticalID, Decimal X, Decimal Y)
        {
            Xcor = X;
            Ycor = Y;
            ShipID = intShipID;
            ShipName = strShipName;
            PoliticalID = intPoliticalID;
        }

        // renames the ship
        public void Rename(string strShipName)
        {
            if (strShipName.Length > 0)      
                ShipName = strShipName;
        }

    }

    // container class for MapObjects
    public class MapObjectContainer
    {

        // create an instance of the Dictionary class to hold all the MapObjects
        Dictionary<int, MapObject> MapObjectList = new Dictionary<int, MapObject>();
        Dictionary<int, Ship> ShipList = new Dictionary<int, Ship>();

        // default constructor
        public MapObjectContainer()
        {
        }

        // function to load ships from database
        public void LoadDBShips()
        {
            // create an instance of the table adaptor for the Ship table
            DamoclesDataSetTableAdapters.ShipTableAdapter ShipTable = new DamoclesDataSetTableAdapters.ShipTableAdapter();

            // this code creates a new data table by calling the get data function of the table adaptor
            DamoclesDataSet.ShipDataTable ShipData = ShipTable.GetDataByGame(1);

            foreach (DamoclesDataSet.ShipRow ShipRow in ShipData)
            {
                Ship objShip = new Ship();
                objShip.ShipID = ShipRow.ShipID;
                objShip.ShipName = ShipRow.ShipName;
                objShip.PoliticalID = ShipRow.PoliticalID;
                objShip.ShipClassID = ShipRow.ShipClassID;
                objShip.Mass = ShipRow.Mass;
                objShip.Xcor = ShipRow.XCor;
                objShip.SystemID = ShipRow.SystemID;
                objShip.Ycor = ShipRow.YCor;

                // add a reference to the ship object to both the ship-specific collection and the overall MapObject collection
                MapObjectList.Add(objShip.ShipID, objShip);
                ShipList.Add(objShip.ShipID, objShip);
            }
        }

        // populate a listbox with ship information, based on PoliticalID
        public void PopulateShipList(ListBox lstShip, int PoliticalID)
        {
            // loop through every ship object
            foreach (KeyValuePair<int,Ship> objShip in ShipList)
            {
                // add a value from the object to the listbox
                if (objShip.Value.PoliticalID == PoliticalID)
                    lstShip.Items.Add(objShip.Value.ShipName);
            }
        }
    }
}

Steve
Title: Re: Programming Advice
Post by: ndkid on October 13, 2010, 11:52:20 AM
I assume that C# programs are setup by default to run in \ProjectDirectory\bin\debug and that is where it expects to find the DB. I could possibly have a DB in a sub-directory but not in a higher directory. What I really need to be able to do is change the datasource from the DB in \ProjectDirectory to the DB in the \ProjectDirectory\bin\debug directory so I don't end up maintaining two versions of the same DB. At the moment though I can't work out how to do that :)

So, a couple of things.
1) It's not that they're set up to run there by default, per se; it's that you're probably running through the IDE (I'm guessing), and it's probably set by default to its "Debug" configuration which, also by default, builds and compiles and executes from bin\debug. All of those are changable options at the project level.
2) You can reference up from the root directory... "..\" would mean to find the file in \bin, and "..\..\" would send it to the project directory. You could use those to go all the way back out to your initial drive, I'd wager, and then mine down to wherever you want. I still suspect you could put a full path where the file name is, as well.
Title: Re: Programming Advice
Post by: ndkid on October 13, 2010, 12:12:15 PM
OO design question, as I have been VB-corrupted over the last few years :)

On program start, all the ships are loaded into a collection within my MapObjectContainer class. I intended to also add all missiles, system bodies, etc to this same collection. However, I have a function to load the names of all the ships of a particular race into a listbox. Of course, my MapObject idea doesn't hold up so well now because I only need to use one value from some of the Ship objects (derived from MapObject). In Aurora this is simply loaded straight from the DB but my new program I intended to keep DB access to an absolute minimum for performance reasons. So if I cycle though the Directory class that is holding my MapObjects, including Ships, and try to use the following code:

            // loop through every map object
            foreach (KeyValuePair<int,MapObject> objShip in mCol)
            {
                // add a value from the object to the listbox
                lstShip.Items.Add(objShip.Value.ShipName);
            }

It doesn't work because the only directly accessible properties are those of the base class (currently Xcor, Ycor and SystemID). As the collections are reference-based, would it be acceptable OO practice to create multiple Directionaries within the container class. One Dictionary for Ships, one for Missiles, one for SystemBodies, etc. but also combined lists such as a Dictionary containing all MapObjects, or all PoweredObjects. In that way, I could perform operations on a specific subset of MapObjects when I needed to but also access them all in a single foreach loop when that was appropriate too. Or is there a more elegant way to achieve this?

Steve

It's certainly true that you could have an object that tracks each type of object separately. And, if the objects share behavior, it probably still makes sense to have a class hierarchy to them. It's also true that, because we're mostly storing references to objects, you could have a MapObject dictionary that contains a ship #2 and also have a ship dictionary that contains ship #2. (Obviously, if ship #2 gets blown up, you'd have to make sure you clean it out of all the necessary lists.)

I'm not sure I have enough data on what you're picturing to give a strong suggestion of what sort of design pattern to use here. My knee-jerk inclination is that, if you have a single MapObject collection, and it hands back mapobjects when asked based on the ID, if you know you're looking for ship #2, and you go ask the mapObjectcollection for MapObject #2, it's perfectly acceptable to then cast that object as a ship and use its ship-specific behaviors. Then, the collection doesn't have to care that it's a ship... just the thing that wants to treat it in a ship-like way does.
Title: Re: Programming Advice
Post by: sloanjh on October 13, 2010, 02:39:19 PM
Found it, although it just has the DB set up as the root directory, which the program thinks is bin\debug. I assume that C# programs are setup by default to run in \ProjectDirectory\bin\debug and that is where it expects to find the DB. I could possibly have a DB in a sub-directory but not in a higher directory. What I really need to be able to do is change the datasource from the DB in \ProjectDirectory to the DB in the \ProjectDirectory\bin\debug directory so I don't end up maintaining two versions of the same DB. At the moment though I can't work out how to do that :)

Steve
Try messing around with the project properties for the project that has your main() in it.  You should find ways to e.g. set the execution path etc.

John
Title: Re: Programming Advice
Post by: Steve Walmsley on October 17, 2010, 05:15:30 AM
I have started a new project file as I have decided to use WPF instead of Windows Forms. Might as well learn everything at once :).

I have imported my class code from the initial Windows Forms project but the error checker doesn't like TableAdapters. Is that because they are not used in WPF applications or am I just missing a reference somewhere?

Steve
Title: Re: Programming Advice
Post by: ndkid on October 17, 2010, 07:01:06 AM
I have imported my class code from the initial Windows Forms project but the error checker doesn't like TableAdapters. Is that because they are not used in WPF applications or am I just missing a reference somewhere?

My first guess: a missing "using" reference in one of the codebehinds. Did you bring the TableAdapter implementation file over directly? If so, what namespace was it using for your table adapter? Is that namespace being used by whatever is creating an instance of that table adapter?
Title: Re: Programming Advice
Post by: Steve Walmsley on October 17, 2010, 07:09:31 AM
The book I am reading at the moment (Visual C# Step by Step by John Sharp) says Don't Use Hungarian Notation. I was just wondering why. Most C# examples I have seen use a variable name format that starts with lower case and has any other words starting with a capital, such as starSystem or systemBody that don't indicate the data type. I am much more comfortable with strPlanetName or intSystemID, or if I was going to avoid a data type prefix I would simply use StarSystem or SystemBody. The wiki entry on Hungarian notes that it was highlighted in Charles Petzold's "Programming Windows" book. This was my Bible for several years so I guess that must be where I picked it up. I was just wondering why it seems so out of favour now and why the lack of capitalization for variable names

I have also ordered "Pro WPF in C# 2010: Windows Presentation Foundation in .NET 4" by Matthew MacDonald so I will be starting to work my way through that as well.

Steve
Title: Re: Programming Advice
Post by: Steve Walmsley on October 17, 2010, 07:16:25 AM
My first guess: a missing "using" reference in one of the codebehinds. Did you bring the TableAdapter implementation file over directly? If so, what namespace was it using for your table adapter? Is that namespace being used by whatever is creating an instance of that table adapter?

The missing using reference was my first thought too but I compared them and they were all the same. My class definitions weren't being picked up either as declarations in the form code were ignoring class definitions in the class files. Something seriously wrong. I have created another new project :) and I will add files normally and copy some of the text rather than transferring whole files. WPF is very different to Windows forms so I am going to work through some books on the client before getting back into the data access.

Steve
Title: Re: Programming Advice
Post by: ndkid on October 17, 2010, 01:05:35 PM
The book I am reading at the moment (Visual C# Step by Step by John Sharp) says Don't Use Hungarian Notation. I was just wondering why. Most C# examples I have seen use a variable name format that starts with lower case and has any other words starting with a capital, such as starSystem or systemBody that don't indicate the data type. I am much more comfortable with strPlanetName or intSystemID, or if I was going to avoid a data type prefix I would simply use StarSystem or SystemBody. The wiki entry on Hungarian notes that it was highlighted in Charles Petzold's "Programming Windows" book. This was my Bible for several years so I guess that must be where I picked it up. I was just wondering why it seems so out of favour now and why the lack of capitalization for variable names

So, a few different philosophical principals are going on here, I think, and probably not all of them are applicable to you.

One is readability-to-others. Most programmers are working in environments where they're not the only developer on a project, so there's a need for different developers to be able to share conventions. For those who don't have that as the case, a lion's share are in situations where, when they leave, someone else is going to have to pick up that code base. As a result, there's a certain democracy to naming conventions. Cocoa, as I've been made to understand by my developer friends who use it, has a community which has developed a sense of "best practices" when it comes to naming convention and usage that tries very hard to be intuitive and consistent.

C#, in particular, has a bit of a muddled life, because it's a endpoint for developers from drastically different backgrounds. The "standard" VB6 habits I noticed when I was doing VB tended to differ greatly from the way-too-shorthand notations that C developers tended to prefer, both of which looked rather different from the semi-Hungarian (that you seem to like) that was most common with C++ developers who'd grown up with MFC and/or COM, which was different still from the it-doesn't-matter-what-the-object-is mentality that came to C++ distilled from the Java folks. Now, C# tends to be the language *all* of those folks (well, except the blue-blood java folks) filter into eventually. (And don't get me started with the code that people who were weaned on old-style ASP write...)

The root of the anti-Hungarian positions, as I understand them, is that naming a variable with a mnemonic attached to a particular Class/Interface is bad for a few particular reasons. It makes the code less readable (in part by making variable names longer). Intellisense has made it less necessary (in a world where you can always alt-space to see what you can call on an object, you don't need the visual prompts).
Perhaps most importantly, it makes it harder to refactor. This is especially true of the various interface glue. Something starts as a checkbox. Then it becomes a radio button. Then it becomes a combo box. In each of those cases, the variable would have to be renamed, or the variable now becomes a mine that will blow up in the face of people who trust it. This is even worse when you design controls that are used in multiple places that expose their members... if you have a form with a
Code: [Select]
public Checkbox ChkAutosave {get;set;}

and you decide to make it a radiobutton, now not only do you have to make that decision inside that form class, you have to go though the same decision process with all of the external files that consume that form. Whereas, if you just have

Code: [Select]
public Checkbox Autosave {get;set;}

changing it from a Checkbox to a radiobutton is far less problematic. (In fact, this example could be improved further, because there's often a push to pass objects via the least necessary interface/class possible, so something lower-level than Checkbox would probably be better to expose, or just the piece of data within the checkbox that actually wants to be accessed.)

For myself, I've had many cases, especially when writing game-logic code, where I've wanted to minimize memory footprints for an object, so I tend to start by assuming that my numeric values can be bytes for any unsigned integer values, shorts for anything signed, and floats for anything rational. Then, as I design, some of those variables end up needing greater range, and get changed upward to ushorts, ints, etc. I'd have to do more find-replace work if I Hugarian-ized those variables, and, frankly, in 95% of the cases that I'm interacting with those variables, their size is irrelevant. (After all, when I start manipulating them, I'm either going to have to check for overflow or assume there is none, or put the operation result in a larger variable type.)
Title: Re: Programming Advice
Post by: sloanjh on October 17, 2010, 11:59:00 PM
The book I am reading at the moment (Visual C# Step by Step by John Sharp) says Don't Use Hungarian Notation. I was just wondering why. Most C# examples I have seen use a variable name format that starts with lower case and has any other words starting with a capital, such as starSystem or systemBody that don't indicate the data type. I am much more comfortable with strPlanetName or intSystemID, or if I was going to avoid a data type prefix I would simply use StarSystem or SystemBody. The wiki entry on Hungarian notes that it was highlighted in Charles Petzold's "Programming Windows" book. This was my Bible for several years so I guess that must be where I picked it up. I was just wondering why it seems so out of favour now and why the lack of capitalization for variable names

I have also ordered "Pro WPF in C# 2010: Windows Presentation Foundation in .NET 4" by Matthew MacDonald so I will be starting to work my way through that as well.

Steve
Here's a post from Joel (On Software) that presents his take on the Hungarian issue.  Note that if you mouse-over a variable (IIRC), Intellisense will tell you the type (if this doesn't work, you can right-click then go to definition), plus the auto-conversions are a lot stricter in C# so Intellisense will also tell you if you try to use a variable incorrectly.

As for books, the two that I like are Hejlsberg's C# book http://www.amazon.com/C-Programming-Language-3rd/dp/0321562992 (I prefer the 3rd edition, since it breaks the new stuff out in a separate section, unfortunately it doesn't have the 4.0 stuff) and (as far as naming conventions etc. go) the Framework Design Guidelines book from the .NET folks http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321246756

I'm not sure where the convention comes from, but a fairly standard naming convention is to use PascalCase for public methods, properties, and classes, camelCase for variables, and a _leadingUnderscore for data members (or sometimes private members - data or function).  IIRC the Framework book discusses this, except they don't like the underscore.  I also disagree with them on when interfaces vs. abstract base classes should be used - I already described my philiosphy in another post.

One more thing - "refactor/rename" is a WONDERFUL feature of the IDE - it makes renames a lot easier and more robust.

John
Title: Re: Programming Advice
Post by: sloanjh on October 18, 2010, 12:26:23 AM
OO design question, as I have been VB-corrupted over the last few years :)

On program start, all the ships are loaded into a collection within my MapObjectContainer class. I intended to also add all missiles, system bodies, etc to this same collection. However, I have a function to load the names of all the ships of a particular race into a listbox. Of course, my MapObject idea doesn't hold up so well now because I only need to use one value from some of the Ship objects (derived from MapObject). In Aurora this is simply loaded straight from the DB but my new program I intend to keep DB access to an absolute minimum for performance reasons. So if I cycle though the Directory class that is holding my MapObjects, including Ships, and try to use the following code:
*SNIP*

Sorry - I didn't see this one when it first went by.  Two comments on OO orthodoxy (or my view of it :-) ):

1)  If you find yourself writing a switch statement, you're doing something non-OO.  The function that gets executed inside the switch statement should probably be a method of an interface (i.e. polymorphic).

2)  If you find yourself writing a lot of functions that have the same code with different types of objects, then you're probably doing something non-OO.  I'm not a big fan of XP (too extreme :-) ), but there's a principle called "Once and Only Once" that is supposed to drive refactorings - when you find yourself writing the same code in two different places, you should do a refactoring so that the shared lines of code show up in a single construct.  The benefit of this is that the third time you need to do it, you'll typically be able to take advantage of the shared code by defining a new class type, rather than by copying the code.

If your particular case, the thing that I suspect is a little stinky (in the "code smells" sense) is that you're going to have a bunch of dictionaries that all look very similar in terms of the way that they're used, but which you have explicitly enumerated.

I just re-read what you want to do, and it sounds to me like you want to do is have certain interfaces (e.g. IMapObject, or IShip) have a dictionary as a static data member, and have each object which implements such an interface register with the dictionary so that it can be looped over (so a ship would register both with IMapObject and with IShip).  This sounds reasonable to me, but it runs into a slight technical flaw in the language - unfortunately (IIRC) C# doesn't allow static members of an interface (tightly coupled to not allowing static virtuals).  This is because of an unfortunate interpretation of "virtual" as meaning "late-bound" on the part of the designers - this leads them to not understand why people are asking for it.  If you follow the pattern I mentioned before of having an "A"(bstract) class for every "I"(nterface), then you can do this in the base class.

Let me know if you want more specific advice on this one (you can PM me if you want) and I can think it through more carefully - probably by rattling off some sample code :-)

John
Title: Re: Programming Advice
Post by: Steve Walmsley on October 22, 2010, 06:40:11 AM
New problem :)

I spent all morning trying to figure out the C# / WPF equivalent of the following code in VB6:

    lstFleet.AddItem rsFleet!FleetName
    lstFleet.ItemData(lstFleet.NewIndex) = rsFleet!FleetID

In the above, rsFleet is a DAO recordset object that contains the result of a query and lstFleet is a listbox. ItemData is a property associated with each listbox entry that allows you to look up based on key rather than value. After two hours of searching online I realised there was no equivalent and figured out that databinding a WPF listbox to a C# collection was the modern way to handle this.

So now I have an object called FleetsList which contains the Fleet data from the database as well as various methods to manipulate that information. Within the FleetsList object is a Dictionary collection called Fleets that holds the actual data. So my code is as follows:

            FleetsList.LoadFleets();
            lstFleet.ItemsSource = FleetsList.Fleets;
            lstFleet.DisplayMemberPath = "FleetName";
            lstFleet.SelectedValuePath = "FleetID";
            MessageBox.Show(lstFleet.Items.Count.ToString());

The Fleet information is loaded without any problems and the Fleets Dictionary object is populated. I set the ItemsSource for the lstFleets Listbox to the Dictionary object and also set the DisplayMemberPath to indicate what should appear in the listbox and the SelectedValuePath to FleetID. Unfortunately, the listbox is blank. However, it does contain the items because the messagebox at the end is showing the correct number of fleets. Also when I add a breakpoint to the following code, I can use debug to look at a selected item by clicking on the listbox and all the data for the entire Fleet record is there. The line fails though due to lstFleet.SelectedValue being null.

        private void lstFleet_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            MessageBox.Show(lstFleet.SelectedValue.ToString());
        }

So my question is, what I am doing wrong? The compiler is happy to accept the syntax but for some reason the DisplayMemberPath and SelectedValuePath are not being set? Also, is there anyway to filter what is displayed in the Listbox so that certain fleets can be excluded, on the basis of RaceID for example.

The relevant line of XAML for the above listbox is:

        <ListBox DataContext="{Binding}" Height="187" ItemsSource="{Binding}" Margin="754,196,27,116" Name="lstFleet" Width="120" SelectionChanged="lstFleet_SelectionChanged" />

Help!!

EDIT: I have managed to display something by overriding the ToString function of Fleet. However, instead of returning just the FleetName it returns in the format of: [8842, Cargo Task Group], where 8842 is the FleetID. No idea why.

        public override string ToString()
        {
            return this.FleetName;
        }

EDIT2: If I remove the override function and also remove the calls to DisplayMemberPath and SelectedValuePath I get the listbox populated with the [8842, Cargo Task Group] format. The overidden function was being called as I put a breakpoint on it but it would seem it is being ignored in favour of the base class implementation for some reason. Now even more confused.

EDIT3: If I put the override back in but replace the FleetName return with "Hello World", the listbox is populated with [8842, Hello World]. I guess the default ouput of a listbox linked to a Dictionary collection must be a KeyValue pair and I am only overriding the Value part. Perhaps there is another function I can overwrite to prevent that but I am reluctant to do so in case I screw up the returning of KeyValue pairs when I really need them. Experimentation contines...

EDIT4: lstFleet.SelectedValuePath = "Key"; instead of lstFleet.SelectedValuePath = "FleetID" successfully returns the FleetID when I select one of the listbox items. Using lstFleet.DisplayMemberPath = "Value" instead of lstFleet.DisplayMemberPath = "FleetName" results in a listbox filled with "Diaspora.Fleet" where Diaspora is the name of the project.

EDIT5: I have tried a data template in XAML instead. Still getting the output of a list of "Diaspora.Fleet". I assume the syntax of Binding Path=Value is the problem. What do I need to use in its place? Really frustrating morning.

        <ListBox DataContext="{Binding}" Height="187" ItemsSource="{Binding}" Margin="637,196,27,116" Name="lstFleet" SelectionChanged="lstFleet_SelectionChanged" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=Value}"></TextBlock>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

Steve
Title: Re: Programming Advice
Post by: ndkid on October 22, 2010, 09:33:00 AM
New problem :)

Okay, the problem here is that you're giving the listbox the dictionary named Fleets. As a result, the listbox can only access the member properties of Dictionary, not the member properties of a Fleet object. If you instead use FleetsList.Fleets.Values, that might get you closer to where you're trying to go, though the ordering may not be what you want. In your place, I'd probably be tempted to structure the code differently, with FleetLists being a class that inherits from the normal dictionary and can then expose the keys and the values differently than the default Dictionary implementation.

When you made "Value" the SelectedValuePath, that meant the type that had ToString called on it was the type of the value of the Dictionary: in this case, Fleet. What you want to do is add an additional function to fleet:

Code: [Select]
public override string ToString()
{
    return this.FleetName;
}

At that point,

Code: [Select]
           FleetsList.LoadFleets();
            lstFleet.ItemsSource = FleetsList.Fleets;
            lstFleet.DisplayMemberPath = "Value";
            lstFleet.SelectedValuePath = "Key";
            MessageBox.Show(lstFleet.Items.Count.ToString());

should probably work. But really, this feels like a bit of a shoehorn. Out of curiosity, do you tend to write design docs or anything, or do you just sort of code as you go? If the latter, do you tend to refactor your data structure as you change what they're expected to do?


EDIT: probably even more readable would be:
Code: [Select]
            FleetsList.LoadFleets();
            lstFleet.ItemsSource = FleetsList.Fleets.Values;
            lstFleet.DisplayMemberPath = "FleetName";
            lstFleet.SelectedValuePath = "FleetId";
            MessageBox.Show(lstFleet.Items.Count.ToString());
Title: Re: Programming Advice
Post by: Steve Walmsley on October 22, 2010, 09:58:47 AM
Okay, the problem here is that you're giving the listbox the dictionary named Fleets. As a result, the listbox can only access the member properties of Dictionary, not the member properties of a Fleet object. If you instead use FleetsList.Fleets.Values, that might get you closer to where you're trying to go, though the ordering may not be what you want. In your place, I'd probably be tempted to structure the code differently, with FleetLists being a class that inherits from the normal dictionary and can then expose the keys and the values differently than the default Dictionary implementation.

When you made "Value" the SelectedValuePath, that meant the type that had ToString called on it was the type of the value of the Dictionary: in this case, Fleet. What you want to do is add an additional function to fleet:

Code: [Select]
public override string ToString()
{
    return this.FleetName;
}

At that point,

Code: [Select]
           FleetsList.LoadFleets();
            lstFleet.ItemsSource = FleetsList.Fleets;
            lstFleet.DisplayMemberPath = "Value";
            lstFleet.SelectedValuePath = "Key";
            MessageBox.Show(lstFleet.Items.Count.ToString());

should probably work. But really, this feels like a bit of a shoehorn. Out of curiosity, do you tend to write design docs or anything, or do you just sort of code as you go? If the latter, do you tend to refactor your data structure as you change what they're expected to do?

I had already tried the above, with the overidden function (see EDIT above), or at least I thought so. I just coded it up again and it worked! I get the list of fleet names. Perhaps I hadn't tried the combination of the overriden function and using Value and Key instead of FleetName and FleetID.


Quote
EDIT: probably even more readable would be:
Code: [Select]
            FleetsList.LoadFleets();
            lstFleet.ItemsSource = FleetsList.Fleets.Values;
            lstFleet.DisplayMemberPath = "FleetName";
            lstFleet.SelectedValuePath = "FleetId";
            MessageBox.Show(lstFleet.Items.Count.ToString());

This option doesn't work. I get the blank listbox again, although as before the items are in there and you can select them. I think the reason I hadn't got it working previously was that I hadn't tried the overidden function at the same time as I tried Value and Key.

Thanks for your help though! Now the next question is how I exclude certain items from the list, or more accurately only include those matching a set of criteria, and how do I order them? Should I do the sorting and selecting in code and then link the listbox to a temporary collection?

Steve
Title: Re: Programming Advice
Post by: Steve Walmsley on October 22, 2010, 10:03:21 AM
Quote
Out of curiosity, do you tend to write design docs or anything, or do you just sort of code as you go? If the latter, do you tend to refactor your data structure as you change what they're expected to do?

I code as I go and have the overall design in my head. Not exactly the way I would work in a group though :). I will rewrite large chunks of code midway through a project if required and refactor the data structures as necessary. For example, last year I spent four months completely rewriting the movement, combat and detection code to use collections in memory rather than regular data access.

One of the reasons for the code as I go approach is I will often have new ideas halfway through a project and rewrite large sections on the fly :)

Steve
Title: Re: Programming Advice
Post by: sloanjh on October 22, 2010, 12:00:24 PM
Don't know if this is relevant or not, but I have a vague recollection of having listboxes not automagically update when the list that they're bound to updates.  This was probably just a newbie mistake on my part (I don't do a lot of Forms programming), but my recollection is that I had to rebind the listbox (to the same list) whenever the contents of the list updated.

John

PS - If the above was unintelligible, it's probably because I'm misremembering the detailed names, i.e. don't waste a lot of time trying to make sense of the above if it doesn't make sense in the first place :-)
Title: Re: Programming Advice
Post by: ndkid on October 22, 2010, 12:31:45 PM
Now the next question is how I exclude certain items from the list, or more accurately only include those matching a set of criteria, and how do I order them? Should I do the sorting and selecting in code and then link the listbox to a temporary collection?

Steve

If you want to filter and such, you have a few general paths I think you can go down, and I'm not sure I know enough about how you are structuring the project to choose one.


1) ADO. If the fleets are being stored in a data source, then you can make the filter part of the call to that datasource, so that you only get back the results you want. There are a number of methods on datasource that can do this (filter, sort, etc), or you can write a query that gets wrapped up all nice and clean by a dataadapter.

2) Dictionary of all fleets that you iterate through. The downside here, of course, is that you can't sort a dictionary, so you'd have to iterate through the entire pile of KeyValuePairs every time you want to filter.

3) Multiple lists. Much as we discussed with the differing MapObjects, you could have one main dictionary that had all fleets, and then have other collections (dictionaries or otherwise) that correspond to particular subsets. This would probably be the fastest way to handle it if you only have one or two filters (race, for example) that you need. It could even be, if you like confusing data structures:

Code: [Select]
Dictionary<Race, Dictionary<int, Fleet>>


Of those, my inclination would be to start by seeing if #1 works for you
Title: Re: Programming Advice
Post by: Steve Walmsley on October 22, 2010, 01:19:59 PM

If you want to filter and such, you have a few general paths I think you can go down, and I'm not sure I know enough about how you are structuring the project to choose one.


1) ADO. If the fleets are being stored in a data source, then you can make the filter part of the call to that datasource, so that you only get back the results you want. There are a number of methods on datasource that can do this (filter, sort, etc), or you can write a query that gets wrapped up all nice and clean by a dataadapter.

2) Dictionary of all fleets that you iterate through. The downside here, of course, is that you can't sort a dictionary, so you'd have to iterate through the entire pile of KeyValuePairs every time you want to filter.

3) Multiple lists. Much as we discussed with the differing MapObjects, you could have one main dictionary that had all fleets, and then have other collections (dictionaries or otherwise) that correspond to particular subsets. This would probably be the fastest way to handle it if you only have one or two filters (race, for example) that you need. It could even be, if you like confusing data structures:

Code: [Select]
Dictionary<Race, Dictionary<int, Fleet>>


Of those, my inclination would be to start by seeing if #1 works for you

One of my design goals for this project is to limit data access as much as possible, as that is the primary performance limitation in Aurora. At the moment I intend to load as much as possible at program start using TableAdapters, manipulate data in memory as much as possible and then save on program close. It sounds like what I should so is create a second collection, one that is suitable for sorting and contains only names and keys, then iterate through Fleets to fill that collection and set that collection as the datasource of the listbox. I assume that would be much faster performance-wise than a data query. As long as the keys are the same, I can use the SelectedValue returned from a selection change on the list box as a key to access data in the main collection. I know it sounds a little over-engineered, which is probably because I am still in the very early phases of learning both WPF and C#, but it should be fast and easy for me to understand :)

Steve
Title: Re: Programming Advice
Post by: ndkid on October 22, 2010, 01:35:00 PM
One of my design goals for this project is to limit data access as much as possible, as that is the primary performance limitation in Aurora. At the moment I intend to load as much as possible at program start using TableAdapters, manipulate data in memory as much as possible and then save on program close. It sounds like what I should so is create a second collection, one that is suitable for sorting and contains only names and keys, then iterate through Fleets to fill that collection and set that collection as the datasource of the listbox. I assume that would be much faster performance-wise than a data query. As long as the keys are the same, I can use the SelectedValue returned from a selection change on the list box as a key to access data in the main collection. I know it sounds a little over-engineered, which is probably because I am still in the very early phases of learning both WPF and C#, but it should be fast and easy for me to understand :)

You may well be right about the performance. I know that, for the enterprise applications I usually am writing code for, there tend to be long discussions about when we ought to do things in memory versus when we ought to let the database do them. It's definitely scenario-dependent. And, of course, we're often using fairly beefy machines to do our DB work, we're making sure our DBs are indexed and tuned, etc, which is a far cry from "I've got an access DB sitting here". :-)

However, one thing I would note is that "datasource" isn't inherently synonymous with "stored on disk". You can have a completely in-memory datasource. You could, in fact, load your fleet table from access into an in-memory data source, and still access that data source with DataAdapters. It might be worth seeing if that pattern is faster or slower than just filtering yourself... I'd imagine it'll depend at least in part on what sort of filters you're applying.
Title: Re: Programming Advice
Post by: sloanjh on October 22, 2010, 08:18:25 PM
However, one thing I would note is that "datasource" isn't inherently synonymous with "stored on disk". You can have a completely in-memory datasource. You could, in fact, load your fleet table from access into an in-memory data source, and still access that data source with DataAdapters. It might be worth seeing if that pattern is faster or slower than just filtering yourself... I'd imagine it'll depend at least in part on what sort of filters you're applying.
Just to pile on with this comment, you can make sql-type queries on arrays etc. in C#.  IIRC, the name for this is LINQ.  I personally haven't used it, but I'm not a DB programmer :-)  Whatever floats your boat :-)

John
Title: Re: Programming Advice
Post by: Steve Walmsley on October 24, 2010, 06:56:04 AM
One of my design goals for this project is to limit data access as much as possible, as that is the primary performance limitation in Aurora. At the moment I intend to load as much as possible at program start using TableAdapters, manipulate data in memory as much as possible and then save on program close. It sounds like what I should so is create a second collection, one that is suitable for sorting and contains only names and keys, then iterate through Fleets to fill that collection and set that collection as the datasource of the listbox. I assume that would be much faster performance-wise than a data query. As long as the keys are the same, I can use the SelectedValue returned from a selection change on the list box as a key to access data in the main collection. I know it sounds a little over-engineered, which is probably because I am still in the very early phases of learning both WPF and C#, but it should be fast and easy for me to understand :)

Sometimes the hardest part of learning a new language is just getting used to completely new concepts. In the above I was trying to find a way to get a key for the Fleet object selected in the listbox so I could look it up in the Fleets collection. What I had overlooked is that the WPF listbox is really just a collection of references to objects. I don't need to get a value from the listbox to look up the correct object in the Fleets collection because the Listbox SelectedItem property is a direct reference to the Fleet object anyway. All the collections involved, the Dictionary FleetsList for all Fleets, my new RaceFleets List collection for the Fleet objects of a particular race that I am using for filtering and the Listbox itself, are all just references to single set of Fleet objects on the heap. Once the Listbox is bound to the RaceFleets collection, the SelectedItem property of the Listbox is all I need because that is the relevant Fleet object.

In fact, now I am starting to get my head around C# and WPF, I probably don't even need the keyvalue pairs provided by the Dictionary object. A simple List would be sufficient as I need to be thinking in terms of object references, not a data-centric style of using Ints as keys. I should make a design decision to use object references throughout rather than database keys.

EDIT: I have gone through and replaced all Dictionary collections with List collections and that has simplied things considerably. Also, because I am now using object references, that has also removed some of the earlier complexity regarding the display of items in listboxes. I have deleted the lines of code that refer to DisplayMemberPath and SelectedValuePath because with a List collection overiding of the Fleet object ToString function is sufficient. Listboxes now display correctly and return references to Fleet Objects or StarSystem objects when items are selected. Definitely a lightbulb moment in my venture into C#/WPF.

Steve
Title: Re: Programming Advice
Post by: sloanjh on October 24, 2010, 10:06:05 AM
In fact, now I am starting to get my head around C# and WPF, I probably don't even need the keyvalue pairs provided by the Dictionary object. A simple List would be sufficient as I need to be thinking in terms of object references, not a data-centric style of using Ints as keys. I should make a design decision to use object references throughout rather than database keys.

This is a really good point - sorry I didn't think to make it earlier :-)

One place to be careful with this is when working with value-classes, which do not live on the managed heap - instead the have the same semantics as a primitive such as int.  It is very easy once you've been working with C# around that operator=() on a value-class object copies the state, rather than copying the reference, which means that changing the copy does not have side-effects on the original.  This can (and has for me) lead to subtle, nasty bugs in code when an object is changed from managed to value, or vice-versa.  This might be a good place to use the Hungarian notation espoused in that Joel article I linked to, and indicate all value-class variables with a "v" in the variable name....

The equivalent issue (value vs. managed) in C++ came up at work a couple of weeks ago - we were trying to decide whether an object in the interface should be a smart pointer or something that looked like a value-class.  We realized that the fundamental question was the following:
Code: [Select]
Foo a(3);
Foo b = a;
a.Set(7);
assert(b.Get() == 3);  // do a and b share state?

If the design of Foo is that the assertion is true for Foo objects, then state is not shared (as is the case with primitives like int) and Foo should be written as a value-class so that the person coding against it has a subliminal cue that state is not shared.  Note that this will typically happen if Foo doesn't have any non-const methods, and so can't be changed once it is created.  Conversely, if the design of Foo is that the assertion is false, then the state is shared, and Foo should be a smart pointer (which is the C++ equivalent of a C# managed class).

EDIT:  Hmmm - I think I just decided that only const classes (with no non-const public methods) should be value-classes in C#.

John

Title: Re: Programming Advice
Post by: sloanjh on October 24, 2010, 10:21:19 AM
In fact, now I am starting to get my head around C# and WPF, I probably don't even need the keyvalue pairs provided by the Dictionary object. A simple List would be sufficient as I need to be thinking in terms of object references, not a data-centric style of using Ints as keys. I should make a design decision to use object references throughout rather than database keys.

While I think this is the right way to think of things, one of the places it's likely to get you into trouble is when you try to persist the data to disk.  At that point, you're going to need identifiers to replace the object references, so you might need integer ids anyway.  I would recommend coding up the save/restore code real soon now (assuming you don't already have it coded up) so that you don't have to retrofit it.

I suspect you'll still want to save/restore using a database, but I wanted to point out two alternatives:
1)  IIRC, there's an ISerializable interface.  I recall looking at it and not having it suite my needs, though.
2)  If you want to read/write XML, you should look at XmlSerializer (might have caps wrong) - it sets it up so you can read/write XML in roughly 5 lines of code.  The object creates its own schema at run-time, along with a parser for the schema.  So rather than parsing the XML into XML data structures in memory which you then have to use to create your class objects (like Xerces (sp?)), it creates your objects directly from the XML.  It's REALLY cool :-)  The downside is that it's not clever enough to manage circular dependencies in the data objects, so you have to create proxy objects (which own integers which are essentially tag ids in a list of objects being refered to, i.e. DB tags) that are used in the objects being xml-ized.  Since you're probably going to have circular dependencies all over the place, this would be a big burden for you.

Information overload, anyone? :-)

John
Title: Re: Programming Advice
Post by: Steve Walmsley on October 24, 2010, 10:47:44 AM
While I think this is the right way to think of things, one of the places it's likely to get you into trouble is when you try to persist the data to disk.  At that point, you're going to need identifiers to replace the object references, so you might need integer ids anyway.  I would recommend coding up the save/restore code real soon now (assuming you don't already have it coded up) so that you don't have to retrofit it.

I have been thinking about saving the objects. I have included the database key as part of the object definition so when I save it I can match the correct database record using ObjectReference.DatabaseKey. Although I am manipulating objects by reference, in most cases the objects still match the database tables. For example, the Universe object contains a List of StarSystems, each of which contains a List of Stars, each of which contains a List of SystemBodies. The List collections match the System, Star and SystemBody tables and I can save the records based on the SystemID, StarID and SystemBodyIDs contained in the individual objects.

Steve

Title: Re: Programming Advice
Post by: sloanjh on October 24, 2010, 11:02:28 AM
While we're on the topic of getting your head around things in C#, it might be useful to mention friendship.  In C#, there's no such thing "friend" keyword.  Note that "friend" is like a loaded gun; it's very powerful when used correctly, but can blow up in your face if used incorrectly.  The correct way to think of a friend class is as an extension of the interface of the original class (I think I got this from one of Robert Martin's excellent design books).  One has to think of the friend this way, since both the friend and the original class have access to the private data of the original.

In C#, we realized that the correct way to do this is to embed the friend class as a public (or private, if you just need a helper) member of the original.  For example:
Code: [Select]
   public partial class Foo
    {
        private List<double> _values;  // private data

        public int Count // this is a const method, since no "set"
        {
            get
            {
                return _values.Count;
            }
        }

        public double Value(int i) // const method (I couldn't remember syntax for op[])
        {
                return _values[i];
        }

        private Foo(List<double> values) // constructor is private to force construction through Builder
        {
            _values = values;
        }
    }

    public partial class Foo
    {
        public class Builder
        {
            private List<double> _bValues = new List<double>();

            public void Add(double value)
            {
                _bValues.Add(value);
            }

            public Foo Create()
            {
                return new Foo(_bValues);
            }
        }
    }
Here I've created a Foo object which is essentially a const list of doubles (I should really implement IEnumerable on it).  It comes with a Builder object, which is the only thing that can create a Foo.  By putting the Add method in the builder, I've extended the interface to allow construction of Foo objects without adding unwanted public methods to Foo.  This trick can also be used to group related classes - for example the concrete enumerator for List<T> is List<T>.Enumerator.  In other words, classes in C# can and should be thought of as mini-namespaces.

One more thing:  I used the "partial" keyword in the class definition of Foo.  That lets me put Foo.Bulder in a different file than Foo, which is useful for avoiding mega-files.

EDIT: Oops - It helps to put the code sample in the right place :-)

John
Title: Re: Programming Advice
Post by: sloanjh on October 24, 2010, 11:07:02 AM
I have been thinking about saving the objects. I have included the database key as part of the object definition so when I save it I can match the correct database record using ObjectReference.DatabaseKey. Although I am manipulating objects by reference, in most cases the objects still match the database tables. For example, the Universe object contains a List of StarSystems, each of which contains a List of Stars, each of which contains a List of SystemBodies. The List collections match the System, Star and SystemBody tables and I can save the records based on the SystemID, StarID and SystemBodyIDs contained in the individual objects.

I suspected you would be on top of this.  I suspect the trouble you're going to have now is keeping the DB tables and the object data members the same as you change the layout of the tables.  I remember reading somewhere a long time ago that OODB design is hard and nasty, but I never understood why - this might be what they were talking about (I haven't done it myself....).

John
Title: Re: Programming Advice
Post by: Steve Walmsley on October 24, 2010, 11:07:19 AM
One place to be careful with this is when working with value-classes, which do not live on the managed heap - instead the have the same semantics as a primitive such as int.  It is very easy once you've been working with C# around that operator=() on a value-class object copies the state, rather than copying the reference, which means that changing the copy does not have side-effects on the original.  This can (and has for me) lead to subtle, nasty bugs in code when an object is changed from managed to value, or vice-versa.  This might be a good place to use the Hungarian notation espoused in that Joel article I linked to, and indicate all value-class variables with a "v" in the variable name....

The equivalent issue (value vs. managed) in C++ came up at work a couple of weeks ago - we were trying to decide whether an object in the interface should be a smart pointer or something that looked like a value-class.  We realized that the fundamental question was the following:
Code: [Select]
Foo a(3);
Foo b = a;
a.Set(7);
assert(b.Get() == 3);  // do a and b share state?

If the design of Foo is that the assertion is true for Foo objects, then state is not shared (as is the case with primitives like int) and Foo should be written as a value-class so that the person coding against it has a subliminal cue that state is not shared.  Note that this will typically happen if Foo doesn't have any non-const methods, and so can't be changed once it is created.  Conversely, if the design of Foo is that the assertion is false, then the state is shared, and Foo should be a smart pointer (which is the C++ equivalent of a C# managed class).

EDIT:  Hmmm - I think I just decided that only const classes (with no non-const public methods) should be value-classes in C#.


At the moment everything is being managed in user-defined objects so I don't think I am using any value-class objects on the stack. For example, all the game data is contained within a Game object, so if I want to reference the current Game Time for example I would use SelectedGame.GameTime, or once I get more advanced I would be making changes to a DateTime object that is part of the Game object using methods that include validation. The only declarations at the level of MainWindow are:

        Universe Galaxy = new Universe();
        FleetList FleetsList = new FleetList();
        RaceList RacesList = new RaceList();
        GameList GamesList = new GameList();
        Game SelectedGame;

SelectedGame is set by cboGame.SelectedItem when the user selects a game from a listbox (which is databound to a collection of Game objects).

Steve
Title: Re: Programming Advice
Post by: sloanjh on October 24, 2010, 11:12:14 AM
At the moment everything is being managed in user-defined objects so I don't think I am using any value-class objects on the stack. For example, all the game data is contained within a Game object, so if I want to reference the current Game Time for example I would use SelectedGame.GameTime, or once I get more advanced I would be making changes to a DateTime object that is part of the Game object using methods that include validation. The only declarations at the level of MainWindow are:

        Universe Galaxy = new Universe();
        FleetList FleetsList = new FleetList();
        RaceList RacesList = new RaceList();
        GameList GamesList = new GameList();
        Game SelectedGame;

SelectedGame is set by cboGame.SelectedItem when the user selects a game from a listbox (which is databound to a collection of Game objects).

Yep - the place it burned me was on some tiny little class - I think it was some sort of identifier object.  Most of your main objects are going to be managed classes, since they're typically big, complex, and intended to be shared among references.  It's the tiny little classes that bite you :-)

John
Title: Re: Programming Advice
Post by: Steve Walmsley on October 25, 2010, 06:25:57 AM
Welcome to today's edition of Steve's Programming Problems...

I have a listbox (lstSystems) bound to a collection of System objects (Galaxy.SystemList). The ToString for System has been overidden to return the System Name. Everything works fine and the listbox displays a list of system names. I would like to sort the systems alphabetically in the listbox. The Items property of the listbox has a collection of SortDescription objects, which is empty at runtime. Therefore I am using the following code snippet to try and sort the listbox on SystemName.

            lstSystems.ItemsSource = Galaxy.SystemList;
            lstSystems.Items.SortDescriptions.Add(new SortDescription("SystemName", ListSortDirection.Ascending));

The sort order does change after the second line is executed and it always changes in the same way, but it certainly isn't anything to do with system name. In fact, despite the fact the sort always happens in the same way I can find no rational explanation for the order of the systems. Any ideas? Am I missing something obvious?

EDIT: Another piece of the puzzle. If I change the ListSortDirection to Descending, I still get the same 'random' order as I do with Ascending!

EDIT2: If I change the second line of code to include a nonsense field name the program compiles and runs with no problem and I still get the same weird sort order. For some reason the fieldname is being ignored.

            lstSystems.Items.SortDescriptions.Add(new SortDescription("TotallyRandomText", ListSortDirection.Ascending));

EDIT3: If I forget all of the above and sort the SystemList collection using the following (which I found but don't really understand), the listbox is sorted correctly (because the collection is sorted not the listbox view of the collection). I would still like to know why the original code doesn't work and why the new code does :)

            SystemList.Sort
                (delegate(StarSystem sys1, StarSystem sys2)
            {
                return Comparer<string>.Default.Compare
                    (sys1.SystemName, sys2.SystemName);
            });

Steve
Title: Re: Programming Advice
Post by: sloanjh on October 25, 2010, 08:38:43 AM
The sort order does change after the second line is executed and it always changes in the same way, but it certainly isn't anything to do with system name. In fact, despite the fact the sort always happens in the same way I can find no rational explanation for the order of the systems. Any ideas? Am I missing something obvious?
All I can say is that this sounds very similar to a past problem I had that I described a few days ago - I couldn't get the ListBox to refresh when the DataSource (?) changed.  Given my experience with ListBox, sorting the list sounds like a good idea, especially if you're going to be doing it a lot (naive sorting costs N^2, and even non-naive will be NlogN).
Quote
I would still like to know why the original code doesn't work and why the new code does :)

            SystemList.Sort
                (delegate(StarSystem sys1, StarSystem sys2)
            {
                return Comparer<string>.Default.Compare
                    (sys1.SystemName, sys2.SystemName);
            });

Steve
"delegate" is C#'s fancy name for "function pointer".  So you're using the built-in sort function of List<T>, and passing it a function pointer (to do the comparisons) that's the equivalent of calling strcmp on the SystemName field of the items.  Hopefully that answers the 2nd question - I haven't a clue for the first.

John
Title: Re: Programming Advice
Post by: Steve Walmsley on October 25, 2010, 09:06:36 AM
There is a Refresh method for the listbox. I am using that to force a data update when I change the datasource. For example:

            lstFleet.ItemsSource = RaceFleets;
            lstFleet.Items.Refresh();

Steve
Title: Re: Programming Advice
Post by: ndkid on October 25, 2010, 09:26:52 AM
Welcome to today's edition of Steve's Programming Problems...

I have a listbox (lstSystems) bound to a collection of System objects (Galaxy.SystemList). The ToString for System has been overidden to return the System Name. Everything works fine and the listbox displays a list of system names. I would like to sort the systems alphabetically in the listbox. The Items property of the listbox has a collection of SortDescription objects, which is empty at runtime. Therefore I am using the following code snippet to try and sort the listbox on SystemName.

            lstSystems.ItemsSource = Galaxy.SystemList;
            lstSystems.Items.SortDescriptions.Add(new SortDescription("SystemName", ListSortDirection.Ascending));

The sort order does change after the second line is executed and it always changes in the same way, but it certainly isn't anything to do with system name. In fact, despite the fact the sort always happens in the same way I can find no rational explanation for the order of the systems. Any ideas? Am I missing something obvious?

Looking at the MSDN reference for SortDescriptions and the SortDescription class (I've never used it before, myself), I get the impression that the properties one can refer to are those of the ListBoxItem, not those of the object referenced by the list box item. If you try:

Code: [Select]
            lstSystems.Items.SortDescriptions.Add(new SortDescription("Content", ListSortDirection.Ascending));

does that do what you want?
Title: Re: Programming Advice
Post by: Steve Walmsley on October 25, 2010, 03:20:18 PM
Looking at the MSDN reference for SortDescriptions and the SortDescription class (I've never used it before, myself), I get the impression that the properties one can refer to are those of the ListBoxItem, not those of the object referenced by the list box item. If you try:

Code: [Select]
            lstSystems.Items.SortDescriptions.Add(new SortDescription("Content", ListSortDirection.Ascending));

does that do what you want?

I haven't tried it yet but that looks like the same type of misunderstanding I had with listboxes. Trying to use a specific rather than generic reference.

Steve
Title: Re: Programming Advice
Post by: Steve Walmsley on October 25, 2010, 03:23:08 PM
And now a simple yet incredibly frustrating problem. All I want to do is add an image to a button. In VB6 this takes about 5 seconds. In C#/WPF it seems to involved creating a resource within the project and then assigning that resource. I cannot get this to work though and I have been trying for about two hours :). Everytime I try to specify an image within XAML I get an error that I am trying to set content twice and I have no idea why. Could anyone provide a very simple example of how to do this?

Steve
Title: Re: Programming Advice
Post by: ndkid on October 25, 2010, 03:33:35 PM
And now a simple yet incredibly frustrating problem. All I want to do is add an image to a button. In VB6 this takes about 5 seconds. In C#/WPF it seems to involved creating a resource within the project and then assigning that resource. I cannot get this to work though and I have been trying for about two hours :). Everytime I try to specify an image within XAML I get an error that I am trying to set content twice and I have no idea why. Could anyone provide a very simple example of how to do this?

I'll quote from http://www.switchonthecode.com/tutorials/creating-buttons-with-xaml-and-csharp:

All that is required to put an image in a button is to add an Image tag inside the Button tag.
Code: [Select]
<Button Name="myButton" Width="75" Height="23" Margin="10,10"
   VerticalAlignment="Bottom" HorizontalAlignment="Right" FontWeight="bold"
   Background="LightBlue" BorderBrush="DarkBlue" Foreground="DarkBlue">
  <Image Source="F:\Images\myImage.png" />
</Button>
You may have noticed that I removed the Content attribute. That's because the Image tag also counts as content. A button can only have the content set once. If you were to leave the the Content attribute and add the Image tag, you would get the following error: "The property 'Content' is set more than once."

And from http://www.vistax64.com/avalon/10390-button-image-not-showing-xaml.html:
In WPF, images must be embedded as Resource - type items. This is different from the WinForms way to use the Resources.resx.
I usually create a folder, say 'Images' in my project - this creates a file folder with the same name on disk, then copy the image(s) into this folder (using windows explorer). Then, go back to Visual Studio. In the project view, right-click on the 'Images' folder and choose 'Add Existing Item' to add the image as a resource (shows as Build Action : Resource in the properties window).
Then simply use e.g. Image Source="Images/mypicture.jpg" im XAML and it should work.
Title: Re: Programming Advice
Post by: Steve Walmsley on October 25, 2010, 03:47:09 PM
I'll quote from http://www.switchonthecode.com/tutorials/creating-buttons-with-xaml-and-csharp:

All that is required to put an image in a button is to add an Image tag inside the Button tag.
Code: [Select]
<Button Name="myButton" Width="75" Height="23" Margin="10,10"
   VerticalAlignment="Bottom" HorizontalAlignment="Right" FontWeight="bold"
   Background="LightBlue" BorderBrush="DarkBlue" Foreground="DarkBlue">
  <Image Source="F:\Images\myImage.png" />
</Button>
You may have noticed that I removed the Content attribute. That's because the Image tag also counts as content. A button can only have the content set once. If you were to leave the the Content attribute and add the Image tag, you would get the following error: "The property 'Content' is set more than once."

And from http://www.vistax64.com/avalon/10390-button-image-not-showing-xaml.html:
In WPF, images must be embedded as Resource - type items. This is different from the WinForms way to use the Resources.resx.
I usually create a folder, say 'Images' in my project - this creates a file folder with the same name on disk, then copy the image(s) into this folder (using windows explorer). Then, go back to Visual Studio. In the project view, right-click on the 'Images' folder and choose 'Add Existing Item' to add the image as a resource (shows as Build Action : Resource in the properties window).
Then simply use e.g. Image Source="Images/mypicture.jpg" im XAML and it should work.

Aha! Thankyou!

Now I get a GIF file to appear, although the button is HUGE, but for some reason it won't load a .ICO file. There is no error and the button is a tiny dot (as I haven't specified a size). Do you know if .ICO files are not acceptable for buttons?

Steve
Title: Re: Programming Advice
Post by: ndkid on October 26, 2010, 09:19:37 AM
Aha! Thankyou!

Now I get a GIF file to appear, although the button is HUGE, but for some reason it won't load a .ICO file. There is no error and the button is a tiny dot (as I haven't specified a size). Do you know if .ICO files are not acceptable for buttons?

If the image starts at the wrong resolution for you, you can probably load it into an image object and resize it. Or you may be able to resize the image as part of the button... I'd have to dig to find out.

I've found one mention of icons on buttons:
http://www.codeproject.com/Messages/1649315/Re-How-to-use-ico-files-in-toolbar-buttons-etc.aspx
Title: Re: Programming Advice
Post by: Steve Walmsley on October 26, 2010, 11:21:40 AM
If the image starts at the wrong resolution for you, you can probably load it into an image object and resize it. Or you may be able to resize the image as part of the button... I'd have to dig to find out.

I've found one mention of icons on buttons:,
http://www.codeproject.com/Messages/1649315/Re-How-to-use-ico-files-in-toolbar-buttons-etc.aspx

I managed to sort it out. One of the major advantages of WPF is that you don't have to specify sizes for controls. The disadvantage of that is the buttons will expand to fit the image. By specifying a size for the button, the image shrinks to fit. So I now have buttons with images working fine. Thanks for the help!

Steve
Title: Re: Programming Advice
Post by: Steve Walmsley on October 27, 2010, 08:18:26 AM
Today's problem is the weirdest so far...

I have some ship graphics listed under Images in the project resource list. I load these at start-up into Image objects so I can access them easily while the program is running and I won't have to load each graphic multiple times. Each time I add some new graphics to the resource list, there are no errors when they load and no errors when I assign the source property of a newly created image to their source property. However, the newly created image is blank on the screen. Then, after a while they will suddenly start working with no apparent reason and no code change. So I add more graphics and they don't work at first. After some time, ranging from a few minutes to a few hours they will start working too.

There must be some sort of refresh happening somewhere in Visual Studio but I have no idea how to manually trigger it. Even quitting and restarting VS doesn't change anything. It's definitely the image rather than the in-game objects because I have tried assigning new images to objects that are aready working, without success (for a while anyway).

The relevant lines of code are:

            // On startup
            imgWarshipFriendly = new Image();
            imgWarshipFriendly.Source = new BitmapImage(new Uri("Images/NavalIcons/WarshipFriendly.png", UriKind.Relative));

            // run-time - only start working after the new resources have been in the project for a while
            image1.Source = imgWarshipFriendly .Source;

Steve
Title: Re: Programming Advice
Post by: ndkid on October 27, 2010, 09:27:18 AM
I have some ship graphics listed under Images in the project resource list. I load these at start-up into Image objects so I can access them easily while the program is running and I won't have to load each graphic multiple times. Each time I add some new graphics to the resource list, there are no errors when they load and no errors when I assign the source property of a newly created image to their source property. However, the newly created image is blank on the screen. Then, after a while they will suddenly start working with no apparent reason and no code change. So I add more graphics and they don't work at first. After some time, ranging from a few minutes to a few hours they will start working too.

There must be some sort of refresh happening somewhere in Visual Studio but I have no idea how to manually trigger it. Even quitting and restarting VS doesn't change anything. It's definitely the image rather than the in-game objects because I have tried assigning new images to objects that are aready working, without success (for a while anyway).

Looking at:
http://msdn.microsoft.com/en-us/library/ms748873.aspx#_displayingimages

and:

http://msdn.microsoft.com/en-us/library/system.windows.controls.image.aspx

I get the impression that you may be conflating the use of BitmapImage and Image. I believe Image is a control that takes as its source a BitmapImage. So it seems like what you probably want to do is load the images into BitmapImage objects and then, as necessary, display them via Image objects.

Here's a discussion that seems like it's aiming for behavior similar to yours:
http://stackoverflow.com/questions/347614/wpf-image-resources

If your display desires get quirky, I may eventually have to suggest you consider using XNA or DirectX, instead of basic WPF stuff. ;-)
Title: Re: Programming Advice
Post by: Steve Walmsley on October 27, 2010, 09:41:31 AM
Looking at:
http://msdn.microsoft.com/en-us/library/ms748873.aspx#_displayingimages

and:

http://msdn.microsoft.com/en-us/library/system.windows.controls.image.aspx

I get the impression that you may be conflating the use of BitmapImage and Image. I believe Image is a control that takes as its source a BitmapImage. So it seems like what you probably want to do is load the images into BitmapImage objects and then, as necessary, display them via Image objects.

Here's a discussion that seems like it's aiming for behavior similar to yours:
http://stackoverflow.com/questions/347614/wpf-image-resources

If your display desires get quirky, I may eventually have to suggest you consider using XNA or DirectX, instead of basic WPF stuff. ;-)

The problem isn't that the code doesn't work, just that it doesn't work for about an hour. If I walk away for an hour and come back, its working and always works after that point. I am wondering why it doesn't work immediately. The code above loads the PNG file into a BitmapImage and then assigns that BitmapImage to the source property of an Image - I am just doing it in one line of code. I got the actual code from MSDN.

Steve
Title: Re: Programming Advice
Post by: ndkid on October 27, 2010, 09:59:52 AM
The problem isn't that the code doesn't work, just that it doesn't work for about an hour. If I walk away for an hour and come back, its working and always works after that point. I am wondering why it doesn't work immediately. The code above loads the PNG file into a BitmapImage and then assigns that BitmapImage to the source property of an Image - I am just doing it in one line of code. I got the actual code from MSDN.

Oh, sorry, misread.

Let's see... I don't have a quick and easy answer, so let's try and confirm the root problem. BitmapImage objects have a downloadcomplete event handler. How about creating a function to handle that event, so you can see whether it's being thrown before you attach the bitmapimage to the image? If you're hitting that, then the slowdown is probably in the image object; if you're not, the slowdown is in the bitmapimage object, and I'll better know where to look for ideas.