Gigi Labs

Please follow Gigi Labs for the latest articles.
Showing posts with label override. Show all posts
Showing posts with label override. Show all posts

Wednesday, November 6, 2013

C#: XML Serialization

Hi guys and gals! :)

In this article, we're going to see how to do XML serialization, which is really just a fancy way of saying we want to load and save objects as files in XML format.

So you see, I've been a big fan of Warcraft 2 for a long time... probably about 15 years. In this game, you command different units (e.g. peasants, knights, etc), and each of them has different attributes such as Damage, Speed, etc. For instance, this is the mage:


Don't mess with that guy - he can turn you into a sheep! And here's the knight... not exactly a mild-mannered fellow, either:


If we represent those units and their attributes in XML, we might end up with something like this (I took out some of the extra crap that appears at the beginning when you actually save a file using XML serialization):

<UnitDatabase>
  <units>
    <Unit>
      <Name>Mage</Name>
      <Armor>0</Armor>
      <MinDamage>5</MinDamage>
      <MaxDamage>9</MaxDamage>
      <Range>2</Range>
      <Sight>9</Sight>
      <Speed>8</Speed>
    </Unit>
    <Unit>
      <Name>Knight</Name>
      <Armor>4</Armor>
      <MinDamage>2</MinDamage>
      <MaxDamage>12</MaxDamage>
      <Range>1</Range>
      <Sight>4</Sight>
      <Speed>13</Speed>
    </Unit>
  </units>
</UnitDatabase>

So, let's see how we can actually read and write a file like this in C#. Create a new Console Application in your favourite IDE.

We first need to create a class to represent our units with their attributes. Create a class and call it Unit. For convenience, we can implement the attributes as auto-implemented properties as follows:

        public String Name { getset; }
        public int Armor { getset; }
        public int MinDamage { getset; }
        public int MaxDamage { getset; }
        public int Range { getset; }
        public int Sight { getset; }
        public int Speed { getset; }

This is just a quick alternative to declaring a member variable and a corresponding read-write property (available from .NET 3.0 onwards). For example, the Name property above is more or less equivalent to the following (just for demonstration - don't actually add it to your code):

        private String name;
       
        public String Name
        {
            get
            {
                return this.name;
            }
            set
            {
                this.name = value;
            }
        }

Next, add a constructor to set the attributes, so we can easily create Unit instances from our main program code:

        public Unit(String name, int armor, int minDamage, int maxDamage, int range, int sight, int speed)
        {
            this.Name = name;
            this.Armor = armor;
            this.MinDamage = minDamage;
            this.MaxDamage = maxDamage;
            this.Range = range;
            this.Sight = sight;
            this.Speed = speed;
        }

Now we need to create another class to hold an array of these units. Create a new class and call it UnitDatabase (admittedly a bit of a poor choice of a name, since it's not actually a database, but anyway). Give it a Units property as follows:

         public Unit[] Units { getset; }

A constructor to assign this directly can also be pretty convenient. Add the following:

        public UnitDatabase(Unit[] units)
        {
            this.Units = units;
        }

Now we can implement our loading and saving code in UnitDatabase itself. Start by adding the code to save the UnitDatabase to a file:

        public void Save(String filename)
        {
            XmlSerializer ser = new XmlSerializer(typeof(UnitDatabase));
           
            using (StreamWriter sw = File.CreateText(filename))
                ser.Serialize(sw, this);
        }

You can see that we're making use of the XmlSerializer class. The file is saved by using its Serialize() method, which takes a TextWriter and the object to serialize. The StreamWriter returned by File CreateText() quite conveniently is a subclass of TextWriter, so we can pass it as the first parameter to Serialize(). The second parameter is this: the UnitDatabase itself.

To get this code to compile, you'll have to add the following using statements at the top:

using System.Xml.Serialization;
using System.IO;

Loading an XML file as a UnitDatabase is just as easy. In this case we make the method static since it isn't tied to any particular UnitDatabase instance:

        public static UnitDatabase Load(String filename)
        {
            XmlSerializer ser = new XmlSerializer(typeof(UnitDatabase));
           
            using (StreamReader sr = File.OpenText(filename))
                return ser.Deserialize(sr) as UnitDatabase;
        }

You can see that we're still using the XmlSerializer, but this time we use the Deserialize() method to read the file from disk and create a UnitDatabase from it. Deserialize() takes a TextReader, which again is a base class of the StreamReader that we get by calling File.OpenText(), so everything fits like magic. Deserialize() returns an Object, so as a last touch we cast this to a UnitDatabase using the as keyword. It's just the same as writing it like this:

                return (UnitDatabase) ser.Deserialize(sr);

That's all we need! Now, let's add some functionality to make it easy to write our units to the console output. All classes inherit from Object, and Object defines this ToString() method which we can use to return a string representation of our objects. This is very convenient in our case, so we can implement Unit's ToString() method as follows:

         public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("Name:      {0}"this.Name);
            sb.AppendLine();
            sb.AppendFormat("Armor:     {0}"this.Armor);
            sb.AppendLine();
            sb.AppendFormat("MinDamage: {0}"this.MinDamage);
            sb.AppendLine();
            sb.AppendFormat("MaxDamage: {0}"this.MaxDamage);
            sb.AppendLine();
            sb.AppendFormat("Range:     {0}"this.Range);
            sb.AppendLine();
            sb.AppendFormat("Sight:     {0}"this.Sight);
            sb.AppendLine();
            sb.AppendFormat("Speed:     {0}"this.Speed);
            sb.AppendLine();
           
            return sb.ToString();
        }

Note the override keyword in the method's signature. This means that we are replacing ToString()'s default functionality (which usually just returns the name of the class) with our own, in this case showing the unit's name and attributes.

Let's do the same for UnitDatabase. In this case we return a concatenation of all the units' string representations:

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            foreach (Unit unit in this.Units)
                sb.AppendLine(unit.ToString());
            return sb.ToString();
        }

To compile this code, you'll need to add the following line at the top of both files (because of the StringBuilder):

using System.Text;

Now all we have left to do is write code in Main() that actually uses these classes. We can start by creating our two units:

            Unit mage = new Unit("Mage"059298);
            Unit knight = new Unit("Knight"42121413);

We can then combine these into an array using collection initializer syntax (see "C# Basics: Morse Code Converter Using Dictionaries" if you forgot what that is):

            Unit[] units = new Unit[] { mage, knight };

Then, we create a UnitDatabase out of this array:

            UnitDatabase db = new UnitDatabase(units);

...and finally save it to a file called units.xml:

            db.Save("units.xml");

You can now press F5 to run the program and see that it works. If you're using Visual Studio, you might have run into this error:


That's because XML serialization needs classes to have an empty constructor. SharpDevelop creates one for you when you create a new class, but Visual Studio does not. So if you're missing those, add them in. One for Unit:

        public Unit()
        {
           
        }

...and one for UnitDatabase:

        public UnitDatabase()
        {
           
        }

Good. Now press F5 to run the program, and then go to the project's bin\Debug folder to check that the units.xml file has been created. When you open it, it should look like this:

<?xml version="1.0" encoding="utf-8"?>
<UnitDatabase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Units>
    <Unit>
      <Name>Mage</Name>
      <Armor>0</Armor>
      <MinDamage>5</MinDamage>
      <MaxDamage>9</MaxDamage>
      <Range>2</Range>
      <Sight>9</Sight>
      <Speed>8</Speed>
    </Unit>
    <Unit>
      <Name>Knight</Name>
      <Armor>4</Armor>
      <MinDamage>2</MinDamage>
      <MaxDamage>12</MaxDamage>
      <Range>1</Range>
      <Sight>4</Sight>
      <Speed>13</Speed>
    </Unit>
  </Units>
</UnitDatabase>

It's got some more stuff in the first two lines that I showed you at the beginning, but that's just added there to make it a valid XML document and you can just ignore it.

At the end of Main(), let us now add code to load the file and display the unit data:

            UnitDatabase loadedDb = UnitDatabase.Load("units.xml");
            Console.WriteLine(loadedDb.ToString());
            Console.ReadLine();

Press F5 to see the result:


If you omit the ToString() as follows:

             Console.WriteLine(loadedDb);

...then the program works all the same, because Console.WriteLine() uses the ToString() method of the objects it is meant to write.

Great! :) In this article, we have seen how we can very easily save (serialize) objects as XML files, and load (deserialize) them back from XML files. To do this we need classes that match the XML structure, as well as the handy XmlSerializer class. Classes to be serialized must have a parameterless constructor. It is possible to do a lot more with XML serialization - there are several attributes that allow you to control the actual XML nodes and attributes that are written to the file.

We have also seen other aspects of C#, such as the ToString() method available in every object; how to override inherited methods; the as keyword which is an elegant alias for type casting; and auto-implemented properties.

Thanks for reading, and come visit Programmer's Ranch again in future! :)

Thursday, August 1, 2013

C# OOP: Abstract classes, fruit, and polymorphism

Hello people from around the world!

The previous article, C# OOP: Queues and Stacks with Inheritance, introduced inheritance and showed that it can help you to reuse methods and variables within one class in another class that derives from it. As I explained in that article, when we say "Queue is a List", we mean it in the sense of "Dog is a mammal".

In practice, inheritance relationships can be a little more elaborate than that. Let's take a classic OOP example - shapes:



So we have a Shape, that is the parent to everything else. It defines a GetArea() method which is expected to be defined in each subclass. The area of a square is calculated based on its length, and that of a circle is calculated based on its radius, each area calculation is specific to that particular shape.

Also, we define a rectangle as a special case of a square. That might sound counterintuitive, but in OOP it makes sense. This is because a rectangle requires more specific attributes than a square (i.e. the width). If we did it the other way round, Square would inherit a width attribute that it doesn't really need.

Anyway, as you can see, it doesn't make any sense for you to have an instance of Shape. You can have an instance of Square, or Circle, or Rectangle, and calculate their area. But how would you calculate the area of a Shape? You can't, because a Shape does not exist.

Let me give you a different example: fruit. You have probably seen apples, oranges, bananas, and many other fruit. But have you ever seen something that is a generic fruit? Things like fruit and shapes are called abstract classes: they give you a general idea of what you can do with them, but they can only be subclassed. They cannot be instantiated.

Let's see a fruity example in action. Start a new SharpDevelop C# console application, and start off with the following class:

    abstract class Fruit
    {
        public String name;
        public abstract void Eat();
    }

Our Fruit has a name, which may be used directly by subclasses. It also declares a method called Eat(), which is abstract. An abstract method is intended to be implemented by subclasses (much like GetArea() in the Shape example), and does not contain any implementation. Abstract methods must be defined in abstract classes, such as Fruit. When a class is abstract, it cannot be instantiated, so trying something like this:

Fruit fruit = new Fruit();

...would cause a compile-time error:


Now, let's add some subclasses to spice things up:

    class Apple : Fruit
    {
        public Apple(String name)
        {
            this.name = name;
        }
       
        public override void Eat()
        {
            Console.WriteLine("You eat the {0}. Crunch crunch."this.name);
        }
       
        public void Throw(String target)
        {
            Console.WriteLine("You throw the {0} at the {1}"this.name, target);
        }
    }
   
    class Orange : Fruit
    {
        public Orange(String name)
        {
            this.name = name;
        }
       
        public override void Eat()
        {
            Console.WriteLine("You eat the {0}. Squish squish."this.name);
        }
       
        public void Squeeze()
        {
            Console.WriteLine("You squeeze the juicy orange into a cup.");
        }
    }

Each subclass conveniently has a constructor that sets the name variable inherited from the parent (Fruit). Each subclass also implements the Eat() method in its own way. The subclasses must provide an implementation for abstract methods they inherit (a compile-time error occurs if you don't). Finally, the Orange and Apple class each implement a method specific to them: Apple has a Throw() method, and Orange has a Squeeze() method. Note: If you're thinking that oranges can also be thrown, Aladdin would disagree:


You can now create instances of Apple or orange and use them as you like:

            Apple apple = new Apple("green apple");
            apple.Eat();
            apple.Throw("guard");

This gives you the following output:


An Apple is always a Fruit. Although you can't instantiate Fruit directly, you can use an Apple as a Fruit:

Fruit appleFruit = new Apple("green apple");

Since Eat() is originally declared in Fruit, you can call it on a Fruit variable without issues:

appleFruit.Eat();

You can't, however, call Throw() from a Fruit because Throw() is declared in Apple, not in Fruit. Doing this:

apple.Throw("guard");

...results in a compile-time error.

The ability to treat different types of Fruit (Apple, Orange) as if they were Fruit allows you to work with them (using the methods and variables provided by Fruit) without needing to know what subclass they are underneath:

            Fruit appleFruit = new Apple("green apple");
            Fruit redAppleFruit = new Apple("red apple");
            Fruit orangeFruit = new Orange("fresh orange");
           
            List<Fruit> myFruit = new List<Fruit>();
            myFruit.Add(appleFruit);
            myFruit.Add(redAppleFruit);
            myFruit.Add(orangeFruit);
           
            foreach (Fruit fruit in myFruit)
                fruit.Eat();

The resulting output is:


This approach is called Polymorphism, and as you can see, it has nothing to do with turning orcs into critters:



A practical example where I've used this is when making games using XNA. You can have many different DrawableGameComponents, each of which may implement its own Draw() method. You can then loop through all your game images (similar to what we did with Fruit) and call Draw() on each of them to draw them on the screen.

Fantastic. In this article we learned about abstract methods, abstract classes, and how to use them polymorphically. An abstract method is a method declaration, without the implementation, that must be implemented in subclasses. Abstract methods are declared in abstract classes, which cannot be instantiated. However, variables of an abstract class type may be used by assigning a subclass. Polymorphism is when we use members of the parent class so that we don't need to distinguish between different subclasses - instead we use the common functionality provided by the parent class.

We've covered quite a bit of ground in OOP, but there is still a lot more to learn: interfaces, overriding, overloading, and, most importantly, encapsulation. Future articles will touch upon these topics as well.