Case Study

We conclude this survival guide with a somewhat larger example that illustrates the concepts we have already discussed and also introduces a few additional features of C#. Our example is a version of the Stock Management System that we have used as a case study throughout the second part of this book. The sample code is in the folder StockManager.

StockDefs.cs

The file StockDefs.cs defines an enumeration, a structure, and an interface that are used in the system.


// StockDefs.cs

using System.Collections;

public enum ItemStatus :  byte
{
   NotFound,   // 0 implicitly
   NotEnough,  // 1 implicitly
   Ok = 5      // explicit value
}

public struct StockItemStruct
{
   public int Id;
   public string Name;
   public int Count;
}

public interface IStock
{
   int AddItem(string name, int count);
   void DeleteItem(int id);
   void RestockItem(int id, int qty);
   ArrayList GetItems(int id);
   ItemStatus BuyItem(int id, int qty, out int avail);
}

INTERFACES

We define an interface IStock that specifies a contract for our Stock Management System, independently of its implementation. Our class Inventory will implement this interface, and it must do so completely and precisely—it must support all the methods of the interface, and each method must have an exact match of the signature.

A class indicates that it implements an interface by using a colon notation, similar to the notation for inheritance.

							public class Inventory : IStock
{
   ...

But while a class can inherit from only a single base class, it can implement multiple interfaces. If a class inherits both from a class and from one or more interfaces, the class is shown first.

public class BookInventory : Inventory, IStock, IAccount
{
   ...

Interfaces are very important in the .NET Framework, which defines many standard interfaces that you can optionally implement in your own classes. Chapter 12 provides an illustration of implementing the .NET interface IComparable using Perl. While you can implement existing interfaces in Perl, at present you cannot define new interfaces using PerlNET. In C# you can both define and implement interfaces.

REF AND OUT PARAMETERS

The standard parameter-passing mechanism in C# is call-by-value (as it is in C and in Perl). This means that a copy is made of arguments to method calls, and if you change an argument in the called method, the change will not be reflected in the method that made the call.

C# supports two other parameter-passing mechanisms. The keyword ref indicates a reference parameter, in which case the parameter inside the method and the corresponding actual argument refer to the same argument. The keyword out refers to an output parameter, which is the same as a reference parameter, except that on the calling side, the parameter need not be assigned prior to the call.

Normally, you can return a value simply as the return value of a method call. But if you want to pass back two or more values, a ref or out parameter is useful. Note that you must use the keyword in the method call as well as in the method definition.

int avail;
ItemStatus status = inven.BuyItem(id, qty, out avail);
if (status == ItemStatus.NotFound)
   Console.WriteLine("Item not found");
...

StockItem.cs

The file StockItem.cs contains the definition of the StockItem class. This class is very similar to other versions of StockItem that we have used elsewhere in the book. It defines the attributes Id, Name, and Count, implemented as public properties with private data members.

// StockItem.cs

public class StockItem
{
   private int m_id;
   private string m_name;
   private int m_count;
   static private int m_nextId = 1;
   public StockItem(string name, int count)
   {
      m_id = m_nextId++;
      m_name = name;
      m_count = count;
   }
   public StockItem(int id)
   {
      m_id = id;
      m_name = "";
      m_count = 0;
   }
   public override bool Equals(object obj)
						{
						StockItem item = (StockItem) obj;
						return (item.m_id == m_id);
						}
						public override int GetHashCode()
						{
						return m_id;
						}
						public override string ToString()
						{
						return m_id + " " + m_name + m_count ;
						}
   public int Id
   {
      get
      {
         return m_id;
      }
   }
   public string Name
   {
      get
      {
         return m_name;
      }
   }
   public int Count
   {
      get
      {
         return m_count;
      }
      set
      {
         m_count = value;
      }
   }
}

STATIC DATA MEMBERS

Ordinary data members of a class have unique data for each object instance. Sometimes it is useful to have a static data member that is shared by all object instances. In the StockItem class we maintain the static variable m_nextId that can be used to automatically generate a new ID every time a new stock item is constructed.

							static private int m_nextId = 1;
public StockItem(string name, int count)
{
   m_id = m_nextId++;
   m_name = name;
   m_count = count;
}
...

OVERRIDING METHODS OF OBJECT

In order to make your class well behaved in the .NET Framework, you should normally override certain methods of the object root class. ToString will return a reasonable string representation of your object instance (default behavior is to return the name of the class). Equals will perform a test of equality of two objects (default behavior is to test for reference equality—which means that two different references that refer to objects with identical data will test out as not equal). GetHashCode calculates an integer hash for your object instance. The C# compiler will issue a warning if you override Equals but not if you override GetHashCode.

We rely on the override of Equals in our code for removing an item from a collection. This topic is discussed more completely in Chapter 13.

Inventory.cs

The file Inventory.cs contains the implementation of the Inventory class, which is a collection of StockItem instances.

// Inventory.cs

using System;
using System.Collections;

public class Inventory : IStock
{
   private ArrayList items;
   public Inventory()
   {
      items = new ArrayList();
      AddItem("Notebook", 10);
      AddItem("Monitor", 20);
   }
   public int AddItem(string name, int count)
   {
      StockItem item = new StockItem(name, count);
      items.Add(item);
      return item.Id;
   }
   public void DeleteItem(int id)
   {
      StockItem item = new StockItem(id);
      items.Remove(item);
   }
   public void RestockItem(int id, int qty)
   {
      StockItem item = FindItem(id);
      if (item != null)
         item.Count += qty;
   }
   public ArrayList GetItems(int id)
   {
      StockItemStruct sitem;
      ArrayList list = new ArrayList();
      if (!CheckId(id) && id != -1)
         return null;
      foreach (StockItem item in items)
      {
         if (id == -1 || id == item.Id)
         {
            sitem.Id = item.Id;
            sitem.Name = item.Name;
            sitem.Count = item.Count;
            list.Add(sitem);
         }
      }
      return list;
   }
   public ItemStatus BuyItem(int id, int qty,
      out int avail)
   {
      avail = 0;
      StockItem item = FindItem(id);
      if (item == null)
         return ItemStatus.NotFound;
      else if (qty <= item.Count)
      {
         item.Count -= qty;
         return ItemStatus.Ok;
      }
      else
      {
         avail = item.Count;
         return ItemStatus.NotEnough;
      }
   }
   private bool CheckId(int id)
   {
      StockItem item = new StockItem(id);
      return items.Contains(item);
   }
   private StockItem FindItem(int id)
   {
      foreach (StockItem item in items)
      {
         if (item.Id == id)
            return item;
      }
      return null;
   }
}

The .NET Framework defines a number of useful collection classes in the namespace System.Collections. A particularly useful class is ArrayList, which is a list of items stored like an array. An array list can be dynamically sized and will grow as necessary to accommodate new elements being added. Collection classes are made up of instances of type object and thus can be used to store any kind of data. Our Inventory class uses an array list to store a list of StockItem instances. The C# foreach loop is a convenient control structure for iterating through the items in a collection.

StockManager.cs

The file StockManager.cs contains code for exercising the Inventory class. It uses the InputWrapper class to simplify console input. By this time the code in this class should be quite understandable. The one new feature is using exceptions in C#.

// StockManager.cs

using System;
using System.Collections;

public class StockManager
{
   public static void Main()
   {
      Inventory inven = new Inventory();
      InputWrapper iw = new InputWrapper();
      string cmd;
      Console.WriteLine(
         "Welcome to Stock Management System");
      Console.WriteLine(
         "For the list of commands type help");
      cmd = iw.getString(">>");
      while (! cmd.Equals("quit"))
      {
         try
         {
            if (cmd.Equals("show"))
            {
               int id = iw.getInt("id (-1 for all): ");
               ShowItemList(inven.GetItems(id));
            }
            else if (cmd.Equals("all"))
            {
               ShowItemList(inven.GetItems(-1));
            }
            else if (cmd.Equals("add"))
            {
               string name = iw.getString("name: ");
               int count = iw.getInt("count: ");
               int id = inven.AddItem(name, count);
               Console.WriteLine("id = {0}", id);
            }
            else if (cmd.Equals("rmv"))
            {
               int id = iw.getInt("id: ");
               inven.DeleteItem(id);
            }
            else if (cmd.Equals("buy"))
            {
               int id = iw.getInt("id: ");
               int qty = iw.getInt("quantity: ");
               int avail;
               ItemStatus status =
                  inven.BuyItem(id, qty, out avail);
               if (status == ItemStatus.NotFound)
                  Console.WriteLine("Item not found");
               else if (status == ItemStatus.NotEnough)
               {
                  Console.WriteLine("Order not filled");
                  Console.WriteLine(
                     "Only {0} items available", avail);
               }
            }
            else
               help();
         }
         catch (Exception e)
						{
						Console.WriteLine("Exception: {0}", e.Message);
						}
         cmd = iw.getString(">> ");
      }
   }
   private static void help()
   {
      Console.WriteLine(
         "The following commands are available:");
      Console.WriteLine(
         "	show      show selected item(s)");
      Console.WriteLine("	all       show all items");
      Console.WriteLine("	add       add an item");
      Console.WriteLine("	rmv       remove an item");
      Console.WriteLine("	quit      exit the program");
   }
   private static void ShowItemList(ArrayList list)
   {
      foreach (StockItemStruct sitem in list)
      {
         string strid = sitem.Id.ToString().PadLeft(4);
         string name = sitem.Name.PadRight(12);
         string strcount =
            sitem.Count.ToString().PadLeft(4);
         string str =
            strid + "   " + name + "   " + strcount;
         Console.WriteLine(str);
      }
   }
}

Here is a sample run, illustrating both some normal operations and some exceptional conditions.

Welcome to Stock Management System
For the list of commands type help
>>all
   1   Notebook         10
   2   Monitor          20
>> add
name: Phone
count: 30
id = 3
>> all
   1   Notebook         10
   2   Monitor          20
   3   Phone            30
>> buy
id: 2
quantity: 5
>> buy
id: 2
quantity: 16
Order not filled
Only 15 items available
>> all
   1   Notebook         10
   2   Monitor          15
   3   Phone            30
>> buy
id: 2
quantity: 1x
Exception: Input string was not in a correct format.
>> buy
id: 2
quantity: 15
>> all
   1   Notebook         10
   2   Monitor           0
   3   Phone            30
>> rmv
id: 2
>> all
   1   Notebook         10
   3   Phone            30
>>

EXCEPTIONS

An inevitable part of programming is dealing with error conditions of various sorts. Our program illustrates the exception-handling mechanism of C#. You enclose a code that may encounter an exception in a try block. You handle the exception in a catch block. Although not shown in our example, you can raise exceptions by a throw statement. The .NET class library provides an Exception class, which you can use to pass information about an exception that occurred. To further specify your exception and to pass additional information, you can derive your own class from Exception. When handling an exception, you may want to throw a new exception. In such a case you can use the “inner exception” feature of the Exception class to pass the original exception on with your new exception.

As discussed in Chapter 12, you can work with exceptions in PerlNET through the eval block and the die function. This chapter also illustrates C# exceptions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.162.110