Common Behaviors of Collections

Introduction

Although you can create a collection class from scratch, most collections classes share some fundamental behaviors and characteristics that you can apply when creating a collection class. To give a skeleton of these default requirements, the .NET Framework provides a few interfaces that you can implement.

Practical LearningPractical Learning: Introducing .NET Collection Classes

  1. Start Microsoft Visual Studio
  2. On the main menu, click File -> New -> Project ...
  3. In the middle frame of the New Project dialog box, click ASP.NET Web Application (.NET Framework) and change the project Name to WaterDistributionCompany4
  4. Click OK
  5. In the New ASP.NET Application dialog box, click the MVC icon and click OK
  6. In the Solution Explorer, right-click WaterDistributionCompany4 -> Add -> New Folder
  7. Type Images and press Enter
  8. Save the following picture to the Images folder:

    Water for a Shining Life

  9. In the Solution Explorer, right-click Content -> Add -> New Item...
  10. In the middle frame of the Add New Item dialog box, click Style Sheet
  11. Change the file Name to WaterDistribution
  12. Click Add
  13. Change the document as follows:
    body {
        background-color: #2b5b8f;
    }
    
    .top-bar  { top:      0px;
                height:   7em;
                width:    100%;
                position: fixed;
                background-color: #203864; }
    
    .logo-bar { margin: auto;
                width:  460px; }
    
    .navbar-inverse   { background-color: #001132;
                        border-top:       3px solid #cfdde0;
                        border-bottom:    3px solid #cfdde0; }
    .navbar-fixed-top { top: 6.75em; }
    
    .jumbotron    { padding-bottom: 4px;
                    background-color: #153a62; }
    .col-md-4 h2  { color: #abcbd9;
                    border-bottom: 1px solid #cfdde0; }
    
    .highligh     { width:            100%;
                    color:            white;
                    background-color: #203864;
                    border:           1px solid white; }
    .lead         { color:       #cfdde0  }
    .col-md-4 p   { color:       #d5d4c2; }
    .copyright    { color:       #beeeab; }
    .push-down    { margin-top:  8em;     }
    .push-down h2 { color:       #d5d4c2; }
    .push-down h3 { color:       #abcbd9; }
    .push-down p  { color:       #cfdde0; }
    .push-down2   { margin-top:  4em;     }
    .left-col     { width:       100px    }
    .ctl-deco     { font-weight: 600;
                    color:       #cfdde0; }
    .large-col    { width:       170px;
                    font-weight: 600;
                    color:       #cfdde0; }
    .xlarge-col   { width:       430px;
                    font-weight: 600;
                    color:       #cfdde0; }
    address       { color:       #cfdde0; }
    
  14. In the Solution Explorer, expand Controllers and double-click HomeController.cs
  15. Change the document as follows:
    using System.Web.Mvc;
    
    namespace WaterDistributionCompany3.Controllers
    {
        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                return View();
            }
    
            public ActionResult About()
            {
                ViewBag.Message = "We supply water to the whole region in a friendly and responsible manner.";
    
                return View();
            }
    
            public ActionResult Contact()
            {
                ViewBag.Message = "Contact us for all your water concerns or issues.";
    
                return View();
            }
        }
    }
  16. Double-click _Layout.cshtml and change the document as follows:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - Water for a Shining Life</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
    <link rel="stylesheet" type="text/css" href="~/Content/WaterDistribution.css" />
    </head>
    <body>
    <div class="top-bar">
        <div class="logo-bar"><img src="~/Images/wsl1.png" alt="Water for a Shining Life" width="490" height="92" /></div>
    </div>
    
    <div class="navbar navbar-inverse navbar-fixed-top">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    @Html.ActionLink("Home", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
                </div>
                <div class="navbar-collapse collapse">
                    <ul class="nav navbar-nav">
                        <li>@Html.ActionLink("Emergency Services", "Index", "Home")</li>
                        <li>@Html.ActionLink("Cummunity", "Index", "Home")</li>
                        <li>@Html.ActionLink("Environment", "Index", "Home")</li>
                        <li>@Html.ActionLink("Resources", "Index", "Home")</li>
                        <li>@Html.ActionLink("Projects", "Index", "Home")</li>
                        <li>@Html.ActionLink("Customer Service", "Index", "Home")</li>
                        <li>@Html.ActionLink("Employment", "Index", "Home")</li>
                        <li>@Html.ActionLink("Questions?", "Index", "Home")</li>
                        <li>@Html.ActionLink("About", "About", "Home")</li>
                        <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
                    </ul>
                </div>
            </div>
        </div>
        <div class="container body-content">
            @RenderBody()
            <hr />
            <footer>
                <p class="copyright text-center">&copy; @DateTime.Now.Year - Water for a Shining Life</p>
            </footer>
        </div>
    
        @Scripts.Render("~/bundles/jquery")
        @Scripts.Render("~/bundles/bootstrap")
        @RenderSection("scripts", required: false)
    </body>
    </html>
  17. In the Solution Explorer, under Views, expand Home, and double-click Index.cshtml
  18. Change the document as follows:
    @{
        ViewBag.Title = "Welcome";
    }
    
    <div class="jumbotron">
        <h2>.</h2>
        <p class="lead">Our water utility company provides energy, greatness, and warmth
            for a everyday life, a shining life. We provide solutions to families, businesses,
            and the community.</p>
    
        <p class="lead">This is the employees portal section of the company. From here,
            employees can register a new water meter, manage a customer account, or
            create a water bill.</p>
    </div>
    
    <div class="row">
        <div class="col-md-4">
            <h2>Water Meters</h2>
            <p>Our company uses the most accurate, sophisticated, and environment-friendly
                water meters on the market. Our suppliers care.</p>
            <p>@Html.ActionLink("New Water Meter", "NewWaterMeter", "Home", new { @class = "btn btn-primary" })</p>
        </div>
        <div class="col-md-4">
            <h2>Customers</h2>
            <p>We supply water to individuals, families, small
            businesses, as well as enterprises or government agencies.</p>
            <p>@Html.ActionLink("New Customer Account", "NewCustomer", "Home", new { @class = "btn btn-primary" })</p>
        </div>
        <div class="col-md-4">
            <h2>Water Bills</h2>
            <p>Our water rates are very competitive nationwide. We use precise, 
                effective, and strict algorithms when performing our water bills 
                calculations.</p>
            <p>@Html.ActionLink("New Water Bill", "FindCustomer", "Home", new { @class = "btn btn-primary" })</p>
        </div>
    </div>
  19. To preview the result, on the main menu, click Debug -> Start Without Debugging:

    Right

  20. Close the browser and return to your programming environment

Introduction to the ICollection Interface

One of the primary pieces of information you should provide abo ut the values in a collection is the number of items a list is (currently) holding. When creating a collection class, to prepare it to provide this valuable information, you can (should) implement an interface named ICollection. The ICollection interface is defined in the System.Collections namespace. The ICollection interface derives from the IEnumerable initerface:

public interface ICollection : IEnumerable

The generic equivalent of the ICollection interface is defined in the System.Collections.Generic namespace. The ICollection<> interface inherits from the IEnumerable<> and the IEnumerable interfaces:

public interface ICollection<T> : IEnumerable<T>, 
								  IEnumerable

Therefore, to start a collection class, you can first implement one of these interfaces. Here is an example for the System.Collections.ICollection<> interface:

using System.Collections.generic;

public class Itemization<T> : ICollection<T>
{
}

Thanks to the flexibility of arrays in the .NET Framework, you can create the items of the collection as an array and give it an initial size. Here is an example:

using System.Collections.Generic;

public class Itemization<T> : ICollection<T>
{
    private int size;
    private T[] items;

    public Itemization()
    {
        size = 0;
        items = new T[10];
    }
}

Implementing ICollection

To assist you with keeping track of the number of items in a collection, the ICollection<T> interface is equipped with an integral property named Count, which you must implement. To do this, you can create a private member variable that will actually keep a count of the number of items. The Count property can then be used to communicate this information to the clients of the class. Here is an example:

using System.Collections.Generic;

public class Itemization<T> : ICollection<T>
{
    private int size;

    public Itemization()
    {
        size = 0;
    }

    public virtual int Count
    {
        get { return size; }
    }
}

The ICollection<> interface also allows its implementer to copy some of its items to an array. To provide this functionality, the interface is equipped with a method named CopyTo, which you must implement. The syntax of this method is:

void CopyTo(T[] array, int arrayIndex);

This method takes two arguments. The first argument is the array that will receive the items. The second argument is the index of the item from where the copying operation will begin. Here is an example:

public class Itemization<T> : ICollection<T>
{
    private int size;
    private T[] items;

    public Itemization()
    {
        size = 0;
        items = new T[10];
    }

    public virtual int Count
    {
        get { return size; }
    }

    public void CopyTo(T[] array, int index)
    {
        T[] values = new T[size];

        for (int i = 0; i < size; i++)
            values[i] = items[i];

        array = values;
    }
}

The System.Collections.ICollection interface extends the IEnumerable interface. The System.Collections.Generic.ICollection<T> interface extends the IEnumerable and the IEnumerable<> interfaces. In the previous lesson, we saw that this means that you should be able to use foreach in your ICollection<>-based collections but you must create the functionality yourself, which is done by implementing the GetEnumerator() method. Because the ICollection<> interface inherits from IEnumerable<> that itself inherits from IEnumerable, you must implement two versions of the GetEnumerator() methods. As we saw already, their syntaxes are:

IEnumerator<T> GetEnumerator();
IEnumerator GetEnumerator();

Even if you don't want to support this feature, you still must provide at least a skeleton for these methods. Here is an example:

public class Itemization<T> : ICollection<T>
{
    private int size;
    private T[] items;

    public Itemization()
    {
        size = 0;
        items = new T[10];
    }

    public virtual int Count
    {
        get { return size; }
    }

    public void CopyTo(T[] array, int index)
    {
        T[] values = new T[size];

        for (int i = 0; i < size; i++)
            values[i] = items[i];

        array = values;
    }

    public IEnumerator<T> GetEnumerator()
    {
        int counter = 0;

        while (counter < Count)
        {
            yield return items[counter];
            counter++;
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        int counter = 0;

        while (counter < Count)
        {
            yield return items[counter];
            counter++;
        }
    }
}

Introduction to the IList Interface

Overview

While the System.Collections.Generic.ICollection<> (and the System.Collections.ICollection) interface provides basic functionality, to assist you in laying out a foundation of a collection class as complete as possible, the .NET Framework provides an interface named IList<> (and its accompanying IList interface). The IList<> interface is defined in the System.Collections.Generic namespace and its non-generic equivalent of the same name is defined in the System.Collections namespace. To use the functionalities of the System.Collections.Generic.IList<> interface necessary to add, insert, get, and delete items from a collection, you must create a class that implements them.

Implementing IList

The System.Collections.IList interface starts as follows:

public interface IList : ICollection, IEnumerable

The System.Collections.Generic.IList<> interface starts as follows:

public interface IList<T> : ICollection<T>,
						    IEnumerable<T>, 
						    IEnumerable

As mentioned above, to create a collection, you can implement the IList<> interface. Here is an example of starting it:

using System.Collections.Generic;

public class Itemization<T> : IList<T>
{
}

This means that the IList<> interface extends the ICollection<>, the IEnumerable<>, and the IEnumerable interfaces. This also implies that you must implement the members of all these parent interfaces, including the Count, the IsReadOnly properties, the CopyTo(), and the GetEnumerator() methods as we saw above. From what we learned with the ICollection interface, here are examples of implementing these members for the System.Collections.IList<T> interface:

public class Itemization<T> : IList<T>
{
    private int size;
    private T[] items;

    public Itemization()
    {
        size = 0;
        items = new T[10];
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        T[] values = new T[size];

        for (int i = 0; i < size; i++)
            values[i] = items[i];

        array = values;
    }

    public int Count
    {
        get { return size; }
    }

    public IEnumerator<T> GetEnumerator()
    {
        int counter = 0;

        while (counter < Count)
        {
            yield return items[counter];
            counter++;
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        int counter = 0;

        while (counter < Count)
        {
            yield return items[counter];
            counter++;
        }
    }
}

As you may know already, while you are implementing an interface, if you are working in Microsoft Visual Studio, it would keep displaying some warnings to remind you about some member(s) you haven't implemented yet. Microsoft Visual Studion can generate skeleton code for your implementation. This would accomplish two goals: 1) it would eliminate the warnings (and errors), 2) it would provide a type of default implementation or a member.

To ask Microsoft Visual Studio to generate code for your interface implementation, start the class in the Code Editor and specify its interface(s). Then:

Practical LearningPractical Learning: Starting an IList Implementation

  1. In the Solution Explorer, right-click Models -> Add -> Class...
  2. Type Collection
  3. Click Add
  4. Change the document as follows:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace WaterDistributionCompany3.Models
    {
        [Serializable]
        public class Collection<T> : IList<T>
        {
        }
    }
  5. Notice that IList<T> is underlined.
    Position the mouse on IList<T>

    Starting an IList Implementation

  6. Click the orange light bulb

    Starting an IList Implementation

  7. In the window that appears, click Implement Interface (if another window appears, in the Preview Changes window, click Apply)
  8. To avoid C# 7 errors (or issues), and to improve the code, change the document as follows:
    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace WaterDistributionCompany3.Models
    {
        [Serializable]
        public class Collection<T> : IList<T>
        {
            // This field will hold the number of items as they are added or removed
            private int counter;
    
            // This array holds the items of the collection
            private T[] items;
    
            // The default constructor is used to initialize a constructor with some default values.
            public Collection()
            {
                counter = 0;
    
                // We will start the collection with a capacity for 10 items
                items = new T[10];
            }
    
            /* IList<T>: This indexed property makes it possible to access EACH item as done in an array. */
            public T this[int index]
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }
    
            /* ICollection<T>: This read-only property holds the total number of items in the collection*/
            public int Count
            {
                get
                {
                    return counter;
                }
            }
    
            /* ICollection<T>: This property would prevent new item addition.*/
            public bool IsReadOnly
            {
                get
                {
                    throw new NotImplementedException();
                }
            }
    
            /* ICollection<T>: This method is used to add a new item to the collection.*/
            public void Add(T item)
            {
                throw new NotImplementedException();
            }
    
            // ICollection<T>: This method deletes all items from the collection
            public void Clear()
            {
                throw new NotImplementedException();
            }
    
            /* ICollection<T>: This method is used to find out whether
             *                 the collection contains the item passed as argument. */
            public bool Contains(T item)
            {
                throw new NotImplementedException();
            }
    
            /* ICollection<T>: This method is used to copy 
             *                 the items of this collection to an array passed as argument. */
            public void CopyTo(T[] array, int arrayIndex)
            {
                throw new NotImplementedException();
            }
    
            /* IEnumerable<T>: Used for iteration */
            public IEnumerator<T> GetEnumerator()
            {
                throw new NotImplementedException();
            }
    
            /* IList<T>: This method checks whether the item
             *           passed as argument exists in the collection. If so, it returns its index. */
            public int IndexOf(T item)
            {
                throw new NotImplementedException();
            }
    
            /* IList<T>: This method can be used to insert an item at a certain position inside the collection. */
            public void Insert(int index, T item)
            {
                throw new NotImplementedException();
            }
    
            /* ICollection<T>: This method first checks the existence of 
             *                 the item passed as argument. If the item exists, the method deletes it. */
            public bool Remove(T item)
            {
                throw new NotImplementedException();
            }
    
            /* IList<T>: This method can be used to delete the item positioned at the index passed as argument. */
            public void RemoveAt(int index)
            {
                throw new NotImplementedException();
            }
    
            /* IEnumerable: Used for iteration */
            IEnumerator IEnumerable.GetEnumerator()
            {
                throw new NotImplementedException();
            }
        }
    }

Populating a Collection

A Read-Only Collection

Most collections are made to receive new values. If you want, you can create a list that cannot receive new values. To support this, the IList interface is equipped with a Boolean property named IsReadOnly. If a list is read-only, it would prevent the clients from adding items to it.

Here is an example of implementing the IsReadOnly property for the System.Collections.IList<T> interface:

public class Itemization<T> : IList<T>
{
    private int size;
    private T[] items;

    public Itemization()
    {
        size = 0;
        items = new T[10];
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        T[] values = new T[size];

        for (int i = 0; i < size; i++)
            values[i] = items[i];

        array = values;
    }

    public int Count
    {
        get { return size; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public IEnumerator<T> GetEnumerator()
    {
        int counter = 0;

        while (counter < Count)
        {
            yield return items[counter];
            counter++;
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        int counter = 0;

        while (counter < Count)
        {
            yield return items[counter];
            counter++;
        }
    }
}

Practical LearningPractical Learning: Setting the Read-Only Effect

Adding an Item

As it should be obvious, the primary operation to perform on a list is to populate it with at least one value or object. To support this operation, the IList<> interface is equipped with a method named Add. Its syntax is:

void Add(T item);

This method takes one argument as the value to add to the collection. If your collection is an array, you can first check that there is still enough room to add a new item. In reality, this is never an issue with the System.Collections.IList interface:

We saw that the Add() method could be defined as follows::

public class Itemization<T> : IList<T>
{
    private int size;
    private T[] items;

    . . .
    
    public int Add(T item)
    {
        this.items[this.size] = item;
        this.size++;

        return size;
    }
}

By default, an array has a fixed size. If you try adding an item in a position higher than the maximum number of items, the compiler would throw an IndexOutOfRangeException exception. Fortunately, we saw that the Array class is equipped with a method named Resize. This allows you to increase the size of an array when you judge it necessary. As a consequence, you can have an array as big as possible.

Practical LearningPractical Learning: Adding an Item to the Collection

  1. Change the code as follows:
    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace WaterDistributionCompany3.Models
    {
        [Serializable]
        public class Collection<T> : IList<T>
        {
            . . . No Change
    
            private void CheckAndIncreaseIfNecessary()
            {
                // If there is not enough room to add a new item (low capacity), create room for 5 additional items.
                if (counter >= items.Length)
                    Array.Resize<T>(ref items, items.Length + 5);
            }
    
            /* ICollection<T>: This method is used to add a new item to the collection.*/
            public void Add(T item)
            {
                // Check whether there is still room in the array.
                // If there is not enough room, then increase the capacity
                CheckAndIncreaseIfNecessary();
    
                // Add the item at the end
                items[counter] = item;
                
                // Increase the current number of items
                counter++;
            }
    
            . . . No Change
        }
    }
  2. In the Solution Explorer, right-click Models -> Add -> Class...
  3. Type WaterMeter as the file Name
  4. Click Add
  5. Change the document as follows:
    using System;
    
    namespace WaterDistributionCompany3.Models
    {
        [Serializable]
        public class WaterMeter
        {
            public string MeterNumber { get; set; }
            public string Make        { get; set; }
            public string Model       { get; set; }
            public string MeterSize   { get; set; }
    
            public override bool Equals(object obj)
            {
                WaterMeter meter = (WaterMeter)obj;
    
                if (meter.MeterNumber == MeterNumber)
                    return true;
    
                return false;
            }
    
            public override int GetHashCode()
            {
                return base.GetHashCode();
            }
        }
    }
  6. In the Solution Explorer, right-click Controllers -> New Scaffolded Item...
  7. Click MVC 5 Controller - Empty and click Add
  8. Type WaterMeters to get WaterMetersController
  9. Click Add
  10. Change the document as follows:
    using System.Web.Mvc;
    
    namespace WaterDistributionCompany40.Controllers
    {
        public class WaterMetersController : Controller
        {
            // GET: WaterMeters
            public ActionResult Index()
            {
                return View();
            }
    
            // GET: WaterMeters/Create
            public ActionResult Create()
            {
                return View();
            }
        }
    }
  11. In the Solution Explorer, under Views, right-click WaterMeters -> Add -> New Scaffolded Item...
  12. In the Add Scaffold dialog box, click MVC 5 View
  13. Click Add
  14. Type Create as the Name of the view
  15. Click Add
  16. Change the file as follows:
    @{
        ViewBag.Title = "Create Water Meter";
    }
    
    <div class="push-down">
        <h2 class="text-center">Create Water Meter</h2>
    </div>
    
    @{
        string strErrorMessage = string.Empty;
    
        FileStream fsWaterMeters = null;
        string strFileWaterMeters = Server.MapPath("~/App_Data/WaterMeters.mtr");
    
        // Create a list of water meters. Since we don't know whether a list of meter waters exists already, let's start with an empty one
        WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter> waterMeters = new WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter>();
        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bfWaterMeters = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
    
        if (IsPost)
        {
            // Make sure a meter number was provided. If not, don't do nothing
            if (string.IsNullOrEmpty(Request["meterNumber"]))
            {
                strErrorMessage = "You must enter a meter number. Otherwise, the water meter account cannot be created.";
            }
            else
            {
                WaterDistributionCompany40.Models.WaterMeter meter = new WaterDistributionCompany40.Models.WaterMeter()
                {
                    MeterNumber = Request["MeterNumber"],
                    Make = Request["Make"],
                    Model = Request["Model"],
                    MeterSize = Request["MeterSize"]
                };
    
                // If a list of water meters was previously created and saved to a file...
                if (File.Exists(strFileWaterMeters))
                {
                    // ... open that file ...
                    using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Open, FileAccess.Read, FileShare.Read))
                    {
                        // ... get the list of water meters
                        waterMeters = (WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter>)bfWaterMeters.Deserialize(fsWaterMeters);
                    }
                }
    
                // Whether the list of water meters is empty or not, add the new water meter to the list
                waterMeters.Add(meter);
    
                // Save the new list of water meters to a file
                using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Create, FileAccess.Write, FileShare.Write))
                {
                    bfWaterMeters.Serialize(fsWaterMeters, waterMeters);
    
                    // Reset the form in case the user wants to create another record
                }
            }
        }
    }
    
    <hr />
    
    <div align="center">
        @using (Html.BeginForm())
        {
            <table>
                <tr>
                    <td class="small ctl-deco"><label for="mNbr">Meter #:</label></td>
                    <td>@Html.TextBox("MeterNumber", "", new { @class = "form-control small" })</td>
                </tr>
                <tr>
                    <td class="ctl-deco"><label for="make"></label>Make:</td>
                    <td>@Html.TextBox("Make", "", new { @class = "form-control medium" })</td>
                </tr>
                <tr>
                    <td class="ctl-deco">Model:</td>
                    <td>@Html.TextBox("Model", "", new { @class = "form-control medium" })</td>
                </tr>
                <tr>
                    <td class="ctl-deco">Meter Size:</td>
                    <td>@Html.TextBox("MeterSize", "", new { @class = "form-control medium" })</td>
                </tr>
                <tr>
                    <td><hr /></td>
                    <td><hr /></td>
                </tr>
                <tr>
                    <td></td>
                    <td>
                        <input type="reset" name="btnReset" value="Reset" class="btn btn-primary medium" />&nbsp;
                        <input type="submit" name="btnSubmit" value="Submit" class="btn btn-primary medium" />
                    </td>
                </tr>
            </table>
        }
            
        <table>
            <tr>
                <td style="width: 120px">&nbsp;</td>
                <td>
                    @Html.ActionLink("Customer Bill Review", "WaterBillStartUp", "BillsProcessing", new { @class = "navbar-brand" })
                    @Html.ActionLink("New Customer", "NewCustomer", "BillsProcessing", new { @class = "navbar-brand" })
                </td>
            </tr>
        </table>
        
        <p class="text-center">@strErrorMessage</p>
    </div>
  17. To execute the project, on the main menu, click Debug -> Start Without Debugging:

    Adding an Item to the Collection

  18. On the top menu of the web page, click Home and click New Water Meter
  19. Type the values on each row of the following table to create a record and click Submit when you have finished each:


    Meter # Make Model Meter Size
    293-740 Breston S-93749 3/4 Inches
    820-418 Vashty Worldwide DD-3840 3/4 Inches
  20. Close the browser and return to your programming environment

Inserting an Item

When you call the Add() method, it adds the new value to the end of the list. Sometimes, you will want the new value to be inserted somewhere inside the list. To support this operation, both the System.Collections.IList and the System.Collections.Generic.IList interfaces provide a method named Insert. The syntax of the System.Collections.IList.Insert() method is:

void Insert(int index, object value);

The syntax of the System.Collections.Generic.IList.Insert() method is:

void Insert(int index, T value);

This method takes two arguments. The second argument is the value or object that will be inserted into the list. The argument must hold a valid value or object. Because the System.Collections.IList.Insert() method takes an object value, if your collection is using a different type of value, you may have to cast it to object. The first argument is the index of the item that will precede the new one.

As mentioned for the System.Collections.IList.Add() method, there are a few things you should know about this operation's success or failure:

The System.Collections.Generic.IList.Insert() method can be implemented as follows:

using System.Collections.Generic;

namespace Exercises.App_Code
{
    public class Itemization<T> : IList<T>
    {
        . . . No Change

        public void Insert(int index, T value)
        {
            // If the index is the same as the current number of items
            // then call Add() and proceed as if we want to add a new item
            if (index >= size)
            {
                Add(value);
                return;
            }

            // If the index is positive but less than the current size,
            // then you need to insert the item inside the collection.
            if ((index >= 0) && (index < size))
            {
                // First, push each item one position up to create
                // an empty space
                for (int i = (size - 1); i > index - 1; i--)
                    items[i + 1] = items[i];

                // Then put the new item in the indicated position
                items[index] = value;
                // Since the new item has been added, increase the 
                // current number of items
                size++;
            }
        }

        public void CopyTo(T[] array, int index)
        {
            . . . No Change
        }

        . . . No Change
    }
}

When calling the IList<>.Insert() method, if you pass an invalid index, the compiler would throw an ArgumentOutOfRangeException.

Locating an Item in the Collection

this Item of a Collection

While using a list, various operations require that you know the object you are currently accessing. To support this operation, the IList and the IList<> interfaces are equipped with an indexed property named Item that you must implement. The Item property of the System.Collection.Generic.IList interface is declared as follows:

T this[int index] { get; set; }

If you implement it right, this property can perform one of three operations:

Here is an example of implementing the indexed property of the IList<> interface:

public class Itemization<T> : IList<T>
{
    private int size;
    private T[] items;

    . . .

    // The index property can be used to add an item to a collection,
    // or to get an item from the collection
    public T this[int index]
    {
        get
        {
            return this.items[index];
        }
        set
        {
            this.items[index] = value;
        }
    }
}

After creating this property, you can then access an item using its index and applying the [] operator on its instance.

Practical LearningPractical Learning: Identifying this Item in the Collection

  1. In the Solution Explorer, under App_Code, double-click Colection.cs to access it
  2. In the top-right combo box of the Code Editor, select this[int index] and change the code of the property as follows:
    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace WaterDistributionCompany4.Models
    {
        [Serializable]
        public class Collection<T> : IList<T>
        {
            . . . No Change
    
            /* IList<T>: This indexed property makes it possible to access EACH item as done in an array. */
            public T this[int index]
            {
                get
                {
                    return items[index];
                }
                set
                {
                    items[index] = value;
                }
            }
    
            . . . No Change
        }
    }

Iterating Through a Collection

One of the most valuable operations to make available to a collection is the abilite to visit each one of its members. As we saw already, this operation is supported by the GetEnumerator() method provided by the IEnumerable<T> interface that each collection class implements.

Practical LearningPractical Learning: Iterating Through a Collection

  1. Access the Collection.cs file and complete the class as follows:
    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace WaterDistributionCompany3.Models
    {
        [Serializable]
        public class Collection<T> : IList<T>
        {
            . . . No Change
    
            /* IList<T>: This indexed property makes it possible to access EACH item as done in an array. */
            public T this[int index]
            {
                get
                {
                    return items[index];
                }
                set
                {
                    items[index] = value;
                }
            }
    
            . . . No Change
    
            private void CheckAndIncreaseIfNecessary()
            {
                // If there is not enough room to add a new item (low capacity), create room for 5 additional items.
                if (counter >= items.Length)
                    Array.Resize<T>(ref items, items.Length + 5);
            }
    
            /* ICollection<T>: This method is used to add a new item to the collection.*/
            public void Add(T item)
            {
                // Check whether there is still room in the array.
                // If there is not enough room, then increase the capacity
                CheckAndIncreaseIfNecessary();
    
                // Add the item at the end
                items[counter] = item;
    
                // Increase the current number of items
                counter++;
            }
    
            . . . No Change
    
            /* IEnumerable<T>: Used for iteration */
            public IEnumerator<T> GetEnumerator()
            {
                int number = 0;
    
                while (number < Count)
                {
                    yield return items[number];
                    number++;
                }
            }
    
            . . . No Change
    
            /* IEnumerable: Used for iteration */
            IEnumerator IEnumerable.GetEnumerator()
            {
                int number = 0;
    
                while (number < Count)
                {
                    yield return items[number];
                    number++;
                }
            }
        }
    }
  2. Click the Create.cshtml tab to access the file and change it as follows:
    @{
        ViewBag.Title = "Create Water Meter";
    }
    
    <div class="push-down">
        <h2 class="text-center">Create Water Meter</h2>
    </div>
    
    @{
        string strErrorMessage = string.Empty;
    
        FileStream fsWaterMeters = null;
        string strFileWaterMeters = Server.MapPath("~/App_Data/WaterMeters.mtr");
    
        // Create a list of water meters. Since we don't know whether a list of meter waters exists already, let's start with an empty one
        WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter> waterMeters = new WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter>();
        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bfWaterMeters = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
    
        if (IsPost)
        {
            // Make sure a meter number was provided. If not, don't do nothing
            if (string.IsNullOrEmpty(Request["meterNumber"]))
            {
                strErrorMessage = "You must enter a meter number. Otherwise, the water meter account cannot be created.";
            }
            else
            {
                WaterDistributionCompany40.Models.WaterMeter meter = new WaterDistributionCompany40.Models.WaterMeter()
                {
                    MeterNumber = Request["MeterNumber"],
                    Make = Request["Make"],
                    Model = Request["Model"],
                    MeterSize = Request["MeterSize"]
                };
    
                // If a list of water meters was previously created and saved to a file...
                if (File.Exists(strFileWaterMeters))
                {
                    // ... open that file ...
                    using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Open, FileAccess.Read, FileShare.Read))
                    {
                        // ... get the list of water meters
                        waterMeters = (WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter>)bfWaterMeters.Deserialize(fsWaterMeters);
                    }
                }
    
                // Whether the list of water meters is empty or not, add the new water meter to the list
                waterMeters.Add(meter);
    
                // Save the new list of water meters to a file
                using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Create, FileAccess.Write, FileShare.Write))
                {
                    bfWaterMeters.Serialize(fsWaterMeters, waterMeters);
    
                    // Reset the form in case the user wants to create another record
                }
            }
        }
    }
    
    <hr />
    
    <div align="center">
        @using (Html.BeginForm())
        {
            <table>
                <tr>
                    <td class="small ctl-deco"><label for="mNbr">Meter #:</label></td>
                    <td>@Html.TextBox("MeterNumber", "", new { @class = "form-control small" })</td>
                </tr>
                <tr>
                    <td class="ctl-deco"><label for="make"></label>Make:</td>
                    <td>@Html.TextBox("Make", "", new { @class = "form-control medium" })</td>
                </tr>
                <tr>
                    <td class="ctl-deco">Model:</td>
                    <td>@Html.TextBox("Model", "", new { @class = "form-control medium" })</td>
                </tr>
                <tr>
                    <td class="ctl-deco">Meter Size:</td>
                    <td>@Html.TextBox("MeterSize", "", new { @class = "form-control medium" })</td>
                </tr>
                <tr>
                    <td><hr /></td>
                    <td><hr /></td>
                </tr>
                <tr>
                    <td></td>
                    <td>
                        <input type="reset" name="btnReset" value="Reset" class="btn btn-primary medium" />&nbsp;
                        <input type="submit" name="btnSubmit" value="Submit" class="btn btn-primary medium" />
                    </td>
                </tr>
            </table>
        }
            
        <table>
            <tr>
                <td style="width: 120px">&nbsp;</td>
                <td>
                    @Html.ActionLink("Customer Bill Review", "WaterBillStartUp", "BillsProcessing", new { @class = "navbar-brand" })
                    @Html.ActionLink("New Customer", "NewCustomer", "BillsProcessing", new { @class = "navbar-brand" })
                </td>
            </tr>
        </table>
        
        <p class="text-center">@strErrorMessage</p>
        
        @{
            strFileWaterMeters = Server.MapPath("~/App_Data/WaterMeters.mtr");
            waterMeters = new WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter>();
    
            if (File.Exists(strFileWaterMeters))
            {
                using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    waterMeters = (WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter>)bfWaterMeters.Deserialize(fsWaterMeters);
    
                    <table style="color: white" border="3">
                        <tr>
                            <td>Meter #</td>
                            <td>Make</td>
                            <td>Model</td>
                            <td>Meter Size</td>
                        </tr>
    
                        @foreach (WaterDistributionCompany40.Models.WaterMeter meter in waterMeters)
                        {
                            <tr>
                                <td>@meter.MeterNumber</td>
                                <td>@meter.Make</td>
                                <td>@meter.Model</td>
                                <td>@meter.MeterSize</td>
                            </tr>
                        }
                    </table>
                }
            }
        }
    </div>
  3. To execute the project, on the main menu, click Debug -> Start Without Debugging:

    Adding an Item to the Collection

  4. Add the following two records:


    Meter # Make Model Meter Size
    627-425 Breston T-39478 5/8 Inches
    304-861 Vashty Worldwide DD-3840 3/4 Inches

    Adding an Item to the Collection

  5. Close the browser and return to your programming environment
  6. Change the Create.cshtml document as follows:
    @{
        ViewBag.Title = "Create Water Meter";
    }
    
    <div class="push-down">
        <h2 class="text-center">Create Water Meter</h2>
    </div>
    
    <hr />
    
    <div align="center">
        @using (Html.BeginForm())
        {
            <table>
                <tr>
                    <td class="small ctl-deco"><label for="mNbr">Meter #:</label></td>
                    <td>@Html.TextBox("MeterNumber", "", new { @class = "form-control small" })</td>
                </tr>
                <tr>
                    <td class="ctl-deco"><label for="make"></label>Make:</td>
                    <td>@Html.TextBox("Make", "", new { @class = "form-control medium" })</td>
                </tr>
                <tr>
                    <td class="ctl-deco">Model:</td>
                    <td>@Html.TextBox("Model", "", new { @class = "form-control medium" })</td>
                </tr>
                <tr>
                    <td class="ctl-deco">Meter Size:</td>
                    <td>@Html.TextBox("MeterSize", "", new { @class = "form-control medium" })</td>
                </tr>
                <tr>
                    <td><hr /></td>
                    <td><hr /></td>
                </tr>
                <tr>
                    <td></td>
                    <td>
                        <input type="reset" name="btnReset" value="Reset" class="btn btn-primary medium" />&nbsp;
                        <input type="submit" name="btnSubmit" value="Submit" class="btn btn-primary medium" />
                    </td>
                </tr>
            </table>
        }
            
        <table>
            <tr>
                <td style="width: 120px">&nbsp;</td>
                <td>
                    @Html.ActionLink("Customer Bill Review", "WaterBillStartUp", "BillsProcessing", new { @class = "navbar-brand" })
                    @Html.ActionLink("New Customer", "NewCustomer", "BillsProcessing", new { @class = "navbar-brand" })
                </td>
            </tr>
        </table>
    </div>
  7. Click the WaterMetersController.cs tab to access it
  8. Change the Create() method as follows:
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Web.Mvc;
    
    namespace WaterDistributionCompany40.Controllers
    {
        public class WaterMetersController : Controller
        {
            // GET: WaterMeters
            public ActionResult Index()
            {
                return View();
            }
    
            // GET: WaterMeters/Create
            public ActionResult Create(string MeterNumber, string MeterSize, string Make, string Model)
            {
                FileStream fsWaterMeters = null;
                BinaryFormatter bfWaterMeters = new BinaryFormatter();
                string strFileWaterMeters = Server.MapPath("~/App_Data/WaterMeters.mtr");
                
                Models.Collection<Models.WaterMeter> waterMeters = new Models.Collection<Models.WaterMeter>();
    
                // Make sure a meter number was provided. If not, don't do nothing
                if (!string.IsNullOrEmpty(MeterNumber))
                {
                    // Create a water meter based on the values gotten from the form
                    Models.WaterMeter meter = new Models.WaterMeter()
                    {
                        MeterNumber = MeterNumber,
                        Make = Make,
                        Model = Model,
                        MeterSize = MeterSize
                    };
    
                    // If a list of water meters was previously created and saved to a file...
                    if (System.IO.File.Exists(strFileWaterMeters))
                    {
                        // ... open that file ...
                        using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Open, FileAccess.Read, FileShare.Read))
                        {
                            // ... get the list of water meters
                            waterMeters = (Models.Collection<Models.WaterMeter>)bfWaterMeters.Deserialize(fsWaterMeters);
                        }
                    }
    
                    // Whether the list of water meters is empty or not, add the new water meter to the list
                    waterMeters.Add(meter);
    
                    // Save the new list of water meters to a file
                    using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Create, FileAccess.Write, FileShare.Write))
                    {
                        bfWaterMeters.Serialize(fsWaterMeters, waterMeters);
                    }
    
                    // In case the user wants to create a new water meter, send him/her back to the form
                    return RedirectToAction("Create");
                }
    
                return View();
            }
        }
    }
  9. In the Solution Explorer, under Views, right-click BillsProcessing -> Add -> View...
  10. Type CreateWaterMeter as the name of the view
  11. Click Add
  12. To execute and test the form, on the main menu, click Debug -> Start Without Debugging:

    Adding an Item to the Collection

  13. Create new records as follows:


    Meter # Make Model Meter Size
    925-935 Igawa International DTT 8802 1 Inch
    779-958 Igawa International DMG 4000 3/4 Inches
  14. Close the browser and return to your programming environment
  15. In the Solution Explorer, right-click Models -> Add -> Class...
  16. Change the file Name to Customer
  17. Click Add
  18. Change the document as follows:
    using System;
    
    namespace WaterDistributionCompany4.Models
    {
        [Serializable]
        public class Customer
        {
            public string AccountNumber { get; set; }
            public string MeterNumber   { get; set; }
            public string FirstName     { get; set; }
            public string LastName      { get; set; }
            public string Address       { get; set; }
            public string City          { get; set; }
            public string County        { get; set; }
            public string State         { get; set; }
            public string ZIPCode       { get; set; }
    
            public override bool Equals(object obj)
            {
                Customer client = (Customer)obj;
    
                if (client.AccountNumber == AccountNumber)
                    return true;
    
                return false;
            }
    
            public override int GetHashCode()
            {
                return base.GetHashCode();
            }
        }
    }
  19. In the Solution Explorer, right-click Controllers -> Add -> New Scaffolded Item...
  20. In the middle frame of the Add Scaffold dialog box, click MVC 5 Controller - Empty
  21. Click Add
  22. Type Customers to get CustomersController
  23. Click Add
  24. Change the document as follows:
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Web.Mvc;
    
    namespace WaterDistributionCompany4.Controllers
    {
        public class CustomersController : Controller
        {
            // GET: Customers
            public ActionResult Index()
            {
                return View();
            }
    
            // GET: Customers/Create
            public ActionResult Create(string AccountNumber, string MeterNumber,
                                            string FirstName, string LastName, string Address,
                                            string City, string County, string State, string ZIPCode)
            {
                FileStream fsCustomers = null;
                BinaryFormatter bfCustomers = new BinaryFormatter();
                string strFileCustomers = Server.MapPath("~/App_Data/Customers.cts");
                Models.Collection<Models.Customer> customers = new Models.Collection<Models.Customer>();
    
                if (!string.IsNullOrEmpty(AccountNumber) && !string.IsNullOrEmpty(MeterNumber))
                {
                    Models.Customer client = new Models.Customer()
                    {
                        AccountNumber = AccountNumber,
                        MeterNumber = MeterNumber,
                        FirstName = FirstName,
                        LastName = LastName,
                        Address = Address,
                        City = City,
                        County = County,
                        State = State,
                        ZIPCode = ZIPCode
                    };
    
                    if (System.IO.File.Exists(strFileCustomers))
                    {
                        using (fsCustomers = new FileStream(strFileCustomers, FileMode.Open, FileAccess.Read, FileShare.Read))
                        {
                            customers = (Models.Collection<Models.Customer>)bfCustomers.Deserialize(fsCustomers);
    
                        }
    
                        customers.Add(client);
    
                        using (fsCustomers = new FileStream(strFileCustomers, FileMode.Create, FileAccess.Write, FileShare.Write))
                        {
                            bfCustomers.Serialize(fsCustomers, customers);
                        }
                    }
    
                    return RedirectToAction("Create");
                }
    
                return View();
            }
        }
    }
  25. In the document, right-click inside the Create() method and click Add View...
  26. In the Add View dialog box, make sure the View Name text box displays Create
    Click Add
  27. Create a form as follows:
    @{
        ViewBag.Title = "New Customer";
    }
    
    <div class="push-down">
        <h2 class="text-center">New Customer</h2>
    </div>
    
    <hr />
    
    <div align="center">
        @using (Html.BeginForm())
        {
            <table style="width: 500px">
                <tr>
                    <td class="left-col ctl-deco">Account #:</td>
                    <td>@Html.TextBox("txtAccountNumber", "", new { style = "width: 200px" })</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                </tr>
                <tr>
                    <td class="ctl-deco">Meter #:</td>
                    <td>@Html.TextBox("txtMeterNumber", "", new { style = "width: 200px" })</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                </tr>
            </table>
            <table style="width: 500px">
                <tr>
                    <td class="left-col ctl-deco">First Name:</td>
                    <td>@Html.TextBox("txtFirstName", "", new { style = "width: 100px" })</td>
                    <td class="ctl-deco">&nbsp;&nbsp;Last Name:</td>
                    <td>@Html.TextBox("txtLastName", "", new { style = "width: 100px" })</td>
                </tr>
                <tr>
                    <td class="ctl-deco">Address:</td>
                    <td colspan="3">@Html.TextBox("txtAddress", "", new { style = "width: 400px" })</td>
                </tr>
                <tr>
                    <td class="ctl-deco">City:</td>
                    <td>@Html.TextBox("txtCity", "", new { style = "width: 100px" })</td>
                    <td class="ctl-deco">&nbsp;&nbsp;County:</td>
                    <td>@Html.TextBox("txtCounty", "", new { style = "width: 100px" })</td>
                </tr>
                <tr>
                    <td class="ctl-deco">State:</td>
                    <td>@Html.TextBox("txtState", "", new { style = "width: 100px" })</td>
                    <td class="ctl-deco">&nbsp;&nbsp;ZIP-Code:</td>
                    <td>@Html.TextBox("txtZIPCode", "", new { style = "width: 100px" })</td>
                </tr>
            </table>
            <hr />
            <table>
                <tr>
                    <td style="width: 120px">&nbsp;</td>
                    <td><input type="reset" name="btnReset" value="Reset" style="width: 120px" /></td>
                    <td>&nbsp;</td>
                    <td><input type="submit" name="btnSubmit" value="Submit" style="width: 120px" /></td>
                </tr>
            </table>
    
            <table>
                <tr>
                    <td style="width: 120px">&nbsp;</td>
                    <td>@Html.ActionLink("New Water Meter", "CreateWaterMeter", "BillsProcessing", new { @class = "navbar-brand" }) 
                        @Html.ActionLink("Customer Bill Review", "WaterBillStartUp", "BillsProcessing", new { @class = "navbar-brand" })</td>
                </tr>
            </table>
        }
    </div>
  28. To execute the project, on the main menu, click Debug -> Start Without Debugging:

    Iterating Through a Collection

  29. Enter the values from each row of the following table and click Submit each time to create the record:

    Account # Meter # First Name Last Name Address City County State ZIP Code
    2958-314-5294 627-425 Nicholas Thorn 2599 Phenicia Road Silver Spring Montgomery MD 20906
    8046-728-5060 304-861 Augustino Derbez 7507 Westchester Ave Washington   DC 20008
    1848-205-3313 925-935 Frank Nunka 13931 Wellington Street College Park Pringe George MD 20740
    4024-850-0482 820-418 Marion Patters 10572 Maya Blvd Frederick   MD 20111
    7029-371-8594 293-740 Danielle Dormand 2515 Guthierez Street Falls Church   VA 22046
  30. Close the browser and return to your programming environment

Checking Whether a Collection Contains a Certain Item

One of the routine operations you can perform on a list is to find out whether it contains a certain value. To assist you with this operation, the IList<> interface is equipped with a method named Contains. Its syntax is:

bool Contains(T item);

This method takes as argument the value to look for. If the value is found in the collection, the method returns true. If the value is not found in the collection, this method returns false. Here is an example of implementing this method:

public class Itemization<T> : IList<T>
{
    private int size;
    private T[] items;

    . . .
    
    public bool Contains(T value)
    {
        for (int i = 0; i < Count; i++)
            if (items[i].Equals(value))
                return true;
        return false;
    }
}

This method calls the Equals() method of the objects that make up the list to find out whether the value argument exists in the collection. If this method produces a wrong result, especially if you are using your own class to represent the item, you should (must) override the Equals() method. Here is an example of a class that indicates that two Employee objects are the same if they have the same employee number (because two employees in the same company/collection should not have the same employee number:

using System.Collections.Generic;

namespace Exercises.App_Code
{
    public class Itemization<T> : IList<T>
    {
        . . . No Change
    }

    public class Employee
    {
        public long   EmployeeNumber { get; set; }
        public string FirstName      { get; set; }
        public string LastName       { get; set; }
        public double HourlySalary   { get; set; }

        public override bool Equals(object obj)
        {
            Employee empl = (Employee)obj;

            if (this.EmployeeNumber == empl.EmployeeNumber)
                return true;

            return false;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
    }
}

Practical LearningPractical Learning: Checking Whether a Collection Contains a Certain Item

Getting the Index of an Item

The Contains() method is used to check whether a particular value (already) exists in the collection. If you know that a certain item exists but you don't know its index inside the list, the IList<> interface can assist you through a method named IndexOf. Its syntax is:

int IndexOf(T item);

This method takes as argument the value to look for in the list. If the value is found in the collection, the method returns its index. If there is no value defined like that, the method returns -1. Here is an example of implementing this method:

public class Itemization<T> : IList<T>
{
    private int size;
    private T[] items;

    . . .
    
    public int IndexOf(T value)
    {
        for (int i = 0; i < Count; i++)
            if (items[i].Equals(value))
                return i;
        return -1;
    }
}

Once again, this method calls the Equals() method of the objects that make up the collection. In most cases, you should make sure you override the Equals() method in the class that T represents.

Practical LearningPractical Learning: Getting the Index of an Item

Getting an Item from a Collection

Getting an item consists of finding and producing it, not necessarily its index or whether it exists. You can write code in your collection class to get the object or you can use a combination of the methods and properties we have already reviewed to locate and produce the item.

Practical LearningPractical Learning: Getting an Item from a Collection

  1. In the Solution Explorer, right-click Models -> Add -> Class...
  2. Set the file Name to WaterBill
  3. Click Add
  4. Change the document as follows:
    using System;
    
    namespace WaterDistributionCompany3.Models
    {
        [Serializable]
        public class WaterBill
        {
            public int      InvoiceNumber         { get; set; }
            public DateTime MeterReadingStartDate { get; set; }
            public DateTime MeterReadingEndDate   { get; set; }
            public int      NumberOfDays          { get; set; }
            public float    CounterReadingStart   { get; set; }
            public float    CounterReadingEnd     { get; set; }
            public float    TotalHCF              { get; set; }
            public int      TotalGallons          { get; set; }
            public double   First15HCF            { get; set; }
            public double   Next10HCF             { get; set; }
            public double   RemainingHCF          { get; set; }
            public double   SewerCharges          { get; set; }
            public double   StormCharges          { get; set; }
            public double   WaterUsageCharges     { get; set; }
            public double   TotalCharges          { get; set; }
            public double   CountyTaxes           { get; set; }
            public double   StateTaxes            { get; set; }
            public DateTime PaymentDueDate        { get; set; }
            public double   AmountDue             { get; set; }
            public DateTime LatePaymentDueDate    { get; set; }
            public double   LateAmountDue         { get; set; }
    
            public override bool Equals(object obj)
            {
                WaterBill bill = (WaterBill)obj;
    
                if (bill.InvoiceNumber == InvoiceNumber)
                    return true;
    
                return false;
            }
    
            public override int GetHashCode()
            {
                return base.GetHashCode();
            }
        }
    }
  5. In the Solution Explorer, right-click Controllers -> Add -> New Scaffolded Item...
  6. In the middle frame of the Add Scaffold dialog box, click MVC 5 Controller - Empty and click Add
  7. Type WaterBills to get WaterBillsController
  8. Press Enter
  9. Change the document as follows:
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Web.Mvc;
    
    namespace WaterDistributionCompany4.Controllers
    {
        public class WaterBillsController : Controller
        {
            // GET: WaterBills
            public ActionResult Index()
            {
                return View();
            }
    
            // GET: WaterBills/WaterBillStartUp
            public ActionResult WaterBillStartUp()
            {
                return View();
            }
    
            // GET: WaterBills/BillPreparation
            public ActionResult BillPreparation(string AccountNumber,
                                                string MeterReadingStartDate, string MeterReadingEndDate,
                                                string CounterReadingStart, string CounterReadingEnd)
            {
                /* We will start our invoices numbers at 1000001.
                 * For now, assuming that there is no bill yet, let's set the starting number at 100000. */
                int invoiceNumber = 100000;
                Models.Customer cust = new Models.Customer();
                BinaryFormatter bfCustomers = new BinaryFormatter();
                BinaryFormatter bfWaterBills = new BinaryFormatter();
                BinaryFormatter bfWaterMeters = new BinaryFormatter();
                string strCustomersFile = Server.MapPath("~/App_Data/Customers.cts");
                string strWaterBillsFile = Server.MapPath("~/App_Data/WaterBills.wbs");
                string strWaterMetersFile = Server.MapPath("~/App_Data/WaterMeters.mtr");
                // We will need a list customers. Since we don't know if one exists already, let's start with an empty one.
                Models.Collection<Models.Customer> customers = new Models.Collection<Models.Customer>();
                Models.Collection<Models.WaterBill> waterBills = new Models.Collection<Models.WaterBill>();
                Models.Collection<Models.WaterMeter> waterMeters = new Models.Collection<Models.WaterMeter>();
    
                // Make sure the user provided all the minimum pieces of information that were requested. //
                if( !string.IsNullOrEmpty(AccountNumber) &&
                    !string.IsNullOrEmpty(MeterReadingStartDate) &&
                    !string.IsNullOrEmpty(MeterReadingEndDate) &&
                    !string.IsNullOrEmpty(CounterReadingStart) &&
                    !string.IsNullOrEmpty(CounterReadingEnd) )
                {
                    // Let's start with a customer whose at least has an account number
                    cust.AccountNumber = AccountNumber;
    
                    // If a list of customers exists in a file, ...
                    if (System.IO.File.Exists(strCustomersFile))
                    {
                        // ... open that file
                        using (FileStream fsCustomers = new FileStream(strCustomersFile, FileMode.Open, FileAccess.Read, FileShare.Read))
                        {
                            // Get the list of customers from the file and put that list in our initial empty list of customers
                            customers = (Models.Collection<Models.Customer>)bfCustomers.Deserialize(fsCustomers);
    
                            /* Since it appears that we currently have a list of customers, let's find out if 
                             * it contains a customer with the account number that was entered in the form. */
                            if (customers.Contains(cust))
                            {
                                // If there is a customer with that number, get the index of that account
                                int index = customers.IndexOf(cust);
    
                                // Get the Customer object stored at that index
                                Models.Customer client = customers[index];
                                
                                /* Now that we have a customer with that account number,
                                 * let's prepare to send the account information to the view. */
                                ViewBag.MeterNumber = client.MeterNumber;
                                ViewBag.FirstName = client.FirstName;
                                ViewBag.LastName = client.LastName;
                                ViewBag.Address = client.Address;
                                ViewBag.City = client.City;
                                ViewBag.County = client.County;
                                ViewBag.State = client.State;
                                ViewBag.ZIPCode = client.ZIPCode;
                            }
                            else
                            {
                                ViewBag.ErrorMessage = "There is no customer with that account number.";
                            }
                        }
    
                        using (FileStream fsWaterMeters = new FileStream(strWaterMetersFile, FileMode.Open, FileAccess.Read, FileShare.Read))
                        {
                            Models.WaterMeter meter = new Models.WaterMeter();
                            waterMeters = (Models.Collection<Models.WaterMeter>)bfWaterMeters.Deserialize(fsWaterMeters);
    
                            meter.MeterNumber = ViewBag.MeterNumber;
    
                            if (waterMeters.Contains(meter))
                            {
                                Models.WaterMeter wm = waterMeters[waterMeters.IndexOf(meter)];
    
                                ViewBag.MeterDetails = wm.MeterNumber + ": " + wm.Make + " " + wm.Model + " (" + wm.MeterSize + ")";
                            }
                            else
                            {
                                ViewBag.ErrorMessage = "There is no water meter with that number.";
                            }
                        }
    
                        // If there exists a file with water bills already, open that file
                        if (System.IO.File.Exists(strWaterBillsFile))
                        {
                            using (FileStream fsWaterBills = new FileStream(strWaterBillsFile, FileMode.Open, FileAccess.Read, FileShare.Read))
                            {
                                // Get the list of water bills from the file and store that list in our initial empty list
                                waterBills = (Models.Collection<Models.WaterBill>)bfWaterBills.Deserialize(fsWaterBills);
    
                                // "Scan" the list from beginning to end to get the last invoice number.
                                foreach (var wb in waterBills)
                                {
                                    invoiceNumber = wb.InvoiceNumber;
                                }
                            }
                        }
    
                        /* Whether there was a list of invoice of numbers already or not, 
                         * increase the current invoice number by 1.
                         * And send the number number to the view. */
                        ViewBag.InvoiceNumber = invoiceNumber + 1;
    
                        /* Let's compute the numbers for the water bill.
                         * The following calculations are not based on anything real.
                         * I used my own rough estimates with weird non-extensive research based on nothing.
                         * These calculations are for demonstrative purposes only for our lesson. */
                        float next10HCF = 0.00f;
                        float first15HCF = 0.00f;
                        float remainingHCF = 0.00f;
                        TimeSpan tsDays = new TimeSpan();
                        DateTime dtMeterReadingEndDate = DateTime.Parse(MeterReadingEndDate);
                        DateTime dtMeterReadingStartDate = DateTime.Parse(MeterReadingStartDate);
    
                        float fCounterReadingStart = float.Parse(CounterReadingStart);
                        float fCounterReadingEnd = float.Parse(CounterReadingEnd);
    
                        tsDays = dtMeterReadingEndDate - dtMeterReadingStartDate;
                        float totalHCF = fCounterReadingEnd - fCounterReadingStart;
                        int totalGallons = (int)(totalHCF * 748.05);
    
                        if(totalHCF <= 15)
                        {
                            first15HCF = totalHCF * 3.612f;
                            next10HCF = 0f;
                            remainingHCF = 0f;
                        }
                        else if(totalHCF <= 25f) {
                            first15HCF = 15f * 3.612f;
                            next10HCF = (totalHCF - 15f) * 3.918f;
                            remainingHCF = 0f;
                        }
                        else
                        {
                            first15HCF = 15f * 3.612f;
                            next10HCF = 10f * 3.918f;
                            remainingHCF = (totalHCF - 25f) * 2.2763f;
                        }
    
                        double waterUsageCharge = first15HCF + next10HCF + remainingHCF;
                        double sewerCharge  = waterUsageCharge * 0.252;
                        double stormCharge  = waterUsageCharge * 0.0025;
                        double totalCharges = waterUsageCharge + sewerCharge + stormCharge;
                        double countyTaxes  = totalCharges * 0.005;
                        double stateTaxes   = totalCharges * 0.0152;
                        double amountDue = totalCharges + countyTaxes + stateTaxes;
    
                        ViewBag.TotalHCF = totalHCF;
                        ViewBag.Next10HCF = next10HCF;
                        ViewBag.AmountDue = amountDue;
                        ViewBag.StateTaxes = stateTaxes;
                        ViewBag.First15HCF = first15HCF;
                        ViewBag.SewerCharge = sewerCharge;
                        ViewBag.StormCharge = stormCharge;
                        ViewBag.CountyTaxes = countyTaxes;
                        ViewBag.NumberOfDays = tsDays.Days;
                        ViewBag.TotalGallons = totalGallons;
                        ViewBag.RemainingHCF = remainingHCF;
                        ViewBag.TotalCharges = totalCharges;
                        ViewBag.LateAmountDue = amountDue + 8.95;
                        ViewBag.WaterUsageCharge = waterUsageCharge;
                        ViewBag.PaymentDueDate = dtMeterReadingEndDate.AddDays(28);
                        ViewBag.LatePaymentDueDate = dtMeterReadingEndDate.AddDays(45);
                    }
                }
    
                return View();
            }
    
            public ActionResult SaveWaterBill(string InvoiceNumber,
                                              string MeterReadingStartDate, string MeterReadingEndDate, string NumberOfDays,
                                              string CounterReadingStart, string CounterReadingEnd, string TotalHCF,
                                              string TotalGallons, string First15HCF, string Next10HCF, string RemainingHCF,
                                              string SewerCharges, string StormCharges, string WaterUsageCharges,
                                              string TotalCharges, string CountyTaxes, string StateTaxes, string PaymentDueDate,
                                              string AmountDue, string LatePaymentDueDate, string LateAmountDue)
            {
                BinaryFormatter bfWaterBills = new BinaryFormatter();
                string strWaterBillsFile = Server.MapPath("~/App_Data/WaterBills.wbs");
                Models.Collection<Models.WaterBill> waterBills = new Models.Collection<Models.WaterBill>();
    
                if (!string.IsNullOrEmpty(InvoiceNumber))
                {
                    Models.WaterBill bill = new Models.WaterBill()
                    {
                        InvoiceNumber = int.Parse(InvoiceNumber),
                        MeterReadingStartDate = DateTime.Parse(MeterReadingStartDate),
                        MeterReadingEndDate = DateTime.Parse(MeterReadingEndDate),
                        NumberOfDays = int.Parse(NumberOfDays),
                        CounterReadingStart = float.Parse(CounterReadingStart),
                        CounterReadingEnd = float.Parse(CounterReadingEnd),
                        TotalHCF = float.Parse(TotalHCF),
                        TotalGallons = int.Parse(TotalGallons),
                        First15HCF = double.Parse(First15HCF),
                        Next10HCF = double.Parse(Next10HCF),
                        RemainingHCF = double.Parse(RemainingHCF),
                        SewerCharges = double.Parse(SewerCharges),
                        StormCharges = double.Parse(StormCharges),
                        WaterUsageCharges = double.Parse(WaterUsageCharges),
                        TotalCharges = double.Parse(TotalCharges),
                        CountyTaxes = double.Parse(CountyTaxes),
                        StateTaxes = double.Parse(StateTaxes),
                        PaymentDueDate = DateTime.Parse(PaymentDueDate),
                        AmountDue = double.Parse(AmountDue),
                        LatePaymentDueDate = DateTime.Parse(LatePaymentDueDate),
                        LateAmountDue = double.Parse(LateAmountDue)
                    };
    
                    if (System.IO.File.Exists(strWaterBillsFile))
                    {
                        using (FileStream fsWaterBills = new FileStream(strWaterBillsFile, FileMode.Open, FileAccess.Read, FileShare.Read))
                        {
                            waterBills = (Models.Collection<Models.WaterBill>)bfWaterBills.Deserialize(fsWaterBills);
                        }
                    }
    
                    waterBills.Add(bill);
    
                    using (FileStream fsWaterBills = new FileStream(strWaterBillsFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))
                    {
                        bfWaterBills.Serialize(fsWaterBills, waterBills);
                    }
                }
    
                return RedirectToAction("WaterBillStartUp");
            }
        }
    }
  10. In the document, right-click inside the WaterBillStartUp() method and click Add View...
  11. In the Add View dialog box, make sure the View Name text box displays WaterBillStartUp and click Add
  12. Change the document as follows:
    @{
        ViewBag.Title = "Water Bill Start-Up";
    }
    
    <div class="push-down">
        <h2 class="text-center">Water Bill Start-Up</h2>
    </div>
    
    <hr />
    
    <div align="center">
        @using (Html.BeginForm("BillPreparation", "BillsProcessing", FormMethod.Post))
        {
            <table>
                <tr>
                    <td class="large-col">Account #:</td>
                    <td>@Html.TextBox("AccountNumber", "", new { @class = "form-control medium" })</td>
                    <td class="large-col">&nbsp;</td>
                    <td>&nbsp;</td>
                </tr>
                <tr>
                    <td class="large-col">Service Start Date:</td>
                    <td>@Html.TextBox("MeterReadingStartDate", "", new { @class = "form-control medium" })</td>
                    <td class="ctl-deco text-right">Service to:</td>
                    <td>@Html.TextBox("MeterReadingEndDate", "", new { @class = "form-control medium" })</td>
    
                </tr>
                <tr>
                    <td class="ctl-deco">Counter Reading Start:</td>
                    <td>@Html.TextBox("CounterReadingStart", "", new { @class = "form-control medium" })</td>
                    <td class="ctl-deco text-right">Counter Reading End:</td>
                    <td>@Html.TextBox("CounterReadingEnd", "", new { @class = "form-control medium" })</td>
                </tr>
            </table>
            <hr />
            <p class="text-center"><input type="submit" name="btnCreateWaterBill" value="Create Water Bill" class="btn btn-primary xlarge" /></p>
        }
    
        <table>
            <tr>
                <td style="width: 120px">&nbsp;</td>
                <td>
                    @Html.ActionLink("New Customer", "Create", "Customers", new { @class = "navbar-brand" })
                    @Html.ActionLink("New Water Meter", "Create", "WaterMeters", new { @class = "navbar-brand" })
                </td>
            </tr>
        </table>
    </div>
  13. In the Solution Explorer, under Views, right-click WaterBills -> Add -> New Scaffolded Item...
  14. In the middle frame of the Add Scaffold dialog box, click MVC 5 View and click Add
  15. Type BillPreparation as the View Name
  16. Click Add
  17. Change the document as follows:
    @{
        ViewBag.Title = "Bill Preparation";
    }
    
    <div class="push-down">
        <h2 class="text-center">Bill Preparation</h2>
    </div>
    
    @{
        string strCity = ViewBag.City;
        string strState = ViewBag.State;
        string strCounty = ViewBag.County;
        string strAddress = ViewBag.Address;
        string strZIPCode = ViewBag.ZIPCode;
        string strLastName = ViewBag.LastName;
        string strFirstName = ViewBag.FirstName;
        string meterDetails = ViewBag.MeterDetails;
    
        float totalHCF = ViewBag.TotalHCF;
        float next10HCF = ViewBag.Next10HCF;
        double amountDue = ViewBag.AmountDue;
        double stateTaxes = ViewBag.StateTaxes;
        double first15HCF = ViewBag.First15HCF;
        int totalGallons = ViewBag.TotalGallons;
        int numberOfDays = ViewBag.NumberOfDays;
        double sewerCharges = ViewBag.SewerCharge;
        double stormCharges = ViewBag.StormCharge;
        double countyTaxes = ViewBag.CountyTaxes;
        float remainingHCF = ViewBag.RemainingHCF;
        int invoiceNumber = ViewBag.InvoiceNumber;
        double totalCharges =    ViewBag.TotalCharges;
        DateTime paymentDueDate = ViewBag.PaymentDueDate;
        double waterUsageCharges = ViewBag.WaterUsageCharge;
        double lateAmountDue = ViewBag.LateAmountDue;
        DateTime latePaymentDueDate = ViewBag.LatePaymentDueDate;
    }
    
    @using (Html.BeginForm("SaveWaterBill", "BillsProcessing", FormMethod.Post))
    {
        <div class="highligh">Customer Account Information</div>
        <br />
        <table>
            <tr>
                <td class="large-col">Account #:</td>
                <td>@Html.TextBox("AccountNumber", "AccountNumber", new { style = "width: 200px" })</td>
                <td>&nbsp;</td>
            </tr>
        </table>
        <table>
            <tr>
                <td class="large-col">Customer Name:</td>
                <td>@Html.TextBox("FirstName", @strFirstName, new { style = "width: 100px" })</td>
                <td>@Html.TextBox("LastName", @strLastName, new { style = "width: 100px" })</td>
            </tr>
        </table>
        <table>
            <tr>
                <td class="large-col"><b>Address:</b></td>
                <td colspan="4">@Html.TextBox("Address", @strAddress, new { style = "width: 500px" })</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>@Html.TextBox("City", @strCity, new { style = "width: 100px" })</td>
                <td>@Html.TextBox("County", @strCounty, new { style = "width: 100px" })</td>
                <td>@Html.TextBox("State", @strState, new { style = "width: 100px" })</td>
                <td>@Html.TextBox("ZIPCode", @strZIPCode, new { style = "width: 100px" })</td>
            </tr>
        </table>
        <br />
        <div class="highligh">Meter Information</div>
        <br />
        <table>
            <tr>
                <td class="large-col">Meter Details:</td>
                <td>@Html.TextBox("MeterNumber", @"MeterNumber", new { style = "width: 100px" })
                    @Html.TextBox("MeterDetails", @meterDetails, new { style = "width: 300px" })</td>
            </tr>
        </table>
        <table>
            <tr>
                <td class="large-col">Service Start Date:</td>
                <td>@Html.TextBox("MeterReadingStartDate", "MeterReadingStartDate", new { style = "width: 100px" })</td>
                <td class="ctl-deco text-right">Service to:</td>
                <td>@Html.TextBox("MeterReadingEndDate", "MeterReadingEndDate", new { style = "width: 100px" })</td>
                <td class="ctl-deco text-right">Number of Days:</td>
                <td>@Html.TextBox("NumberOfDays", @numberOfDays, new { style = "width: 100px" })</td>
            </tr>
            <tr>
                <td class="ctl-deco">Counter Reading Start:</td>
                <td>@Html.TextBox("CounterReadingStart", @"CounterReadingStart", new { style = "width: 100px" })</td>
                <td class="ctl-deco text-right">Counter Reading End:</td>
                <td>@Html.TextBox("CounterReadingEnd", @"CounterReadingEnd", new { style = "width: 100px" })</td>
                <td class="ctl-deco text-right">Total HCF:</td>
                <td>@Html.TextBox("TotalHCF", @totalHCF.ToString("F"), new { style = "width: 100px" })</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>&nbsp;</td>
                <td>&nbsp;</td>
                <td>&nbsp;</td>
                <td class="ctl-deco text-right">Total Gallons:</td>
                <td>@Html.TextBox("TotalGallons", @totalGallons, new { style = "width: 100px" })</td>
            </tr>
            <tr>
                <td class="ctl-deco">1st 15 HCF at $3.6121:</td>
                <td>@Html.TextBox("First15HCF", @first15HCF.ToString("F"), new { style = "width: 100px" })</td>
                <td class="ctl-deco">Next 10 HCF at $3.9180:</td>
                <td class="text-right">@Html.TextBox("Next10HCF", @next10HCF.ToString("F"), new { style = "width: 100px" })</td>
                <td class="ctl-deco">Remaining HCF at $4.2763:</td>
                <td>@Html.TextBox("RemainingHCF", @remainingHCF.ToString("F"), new { style = "width: 100px" })</td>
            </tr>
            <tr>
                <td class="ctl-deco">Sewer Charges:</td>
                <td>@Html.TextBox("SewerCharges", @sewerCharges.ToString("F"), new { style = "width: 100px" })</td>
                <td class="ctl-deco text-right">Storm Charges:</td>
                <td class="text-right">@Html.TextBox("StormCharges", @stormCharges.ToString("F"), new { style = "width: 100px" })</td>
                <td class="ctl-deco text-right">Water Usage Charges:</td>
                <td>@Html.TextBox("WaterUsageCharges", @waterUsageCharges.ToString("F"), new { style = "width: 100px" })</td>
            </tr>
            <tr>
                <td></td>
                <td>&nbsp;</td>
                <td>&nbsp;</td>
                <td>&nbsp;</td>
                <td class="ctl-deco text-right">Total Charges:</td>
                <td>@Html.TextBox("TotalCharges", @totalCharges.ToString("F"), new { style = "width: 100px" })</td>
            </tr>
        </table>
        <br />
        <div class="highligh">Water Bill Values</div>
        <br />
        <table>
            <tr>
                <td class="xlarge-col text-right">County Taxes:</td>
                <td>@Html.TextBox("CountyTaxes", @countyTaxes.ToString("F"), new { style = "width: 100px" })</td>
                <td class="large-col text-right">State Taxes:</td>
                <td>@Html.TextBox("StateTaxes", @stateTaxes.ToString("F"), new { style = "width: 100px" })</td>
            </tr>
        </table>
        <hr />
        <table>
            <tr>
                <td class="xlarge-col text-right">Payment Due Date:</td>
                <td>@Html.TextBox("PaymentDueDate", @paymentDueDate.ToShortDateString(), new { style = "width: 100px" })</td>
                <td class="large-col text-right">Amount Due:</td>
                <td>@Html.TextBox("AmountDue", @amountDue.ToString("F"), new { style = "width: 100px" })</td>
            </tr>
            <tr>
                <td class="xlarge-col text-right">Late Payment Due Date:</td>
                <td>@Html.TextBox("LatePaymentDueDate", @latePaymentDueDate.ToShortDateString(), new { style = "width: 100px" })</td>
                <td class="large-col text-right ">Late Payment Amount:</td>
                <td>@Html.TextBox("LateAmountDue", @lateAmountDue.ToString("F"), new { style = "width: 100px" })</td>
            </tr>
        </table>
        <hr />
        <table>
            <tr>
                <td class="ctl-deco">Invoice #:</td>
                <td>@Html.TextBox("InvoiceNumber", @invoiceNumber, new { style = "width: 100px" })</td>
                <td class="xlarge-col">&nbsp;</td>
                <td class="text-right"><input type="submit" name="btnSaveWaterBill" value="Save Water Bill" style="width: 300px" /></td>
            </tr>
        </table>
    }
  18. In the Solution Explorer, under Views, right-click Home -> Add -> View...
  19. Type SaveWaterBill as the name of the view
  20. Click Add
  21. Click the WaterBillStartUp.cshtml tab to activate it
  22. To execute the project, on the main menu, click Debug -> Start Without Debugging:

    Getting an Item from a Collection

  23. In the text boxes, enter some values as follows:

    Account # 1848-205-3313
    Service Start Date 1/12/2018
    Service To 4/17/2018
    Meter Reading Start 274.68
    Reading End 316.53

    Getting an Item from a Collection

  24. Click the Create Watter Bill button:

    Getting an Item from a Collection

  25. Verify the bill summary and click the Save Water Bill button
  26. Enter some values as follows:

    Account # 2958-314-5294
    Service Start Date 1/18/2018
    Service To 4/17/2018
    Meter Reading Start 5827.59
    Reading End 5883.17
  27. Click Create Water Bill:

    Getting an Item from a Collection

  28. Click the Save Water Bitt button
  29. In the text boxes, enter some values as follows:

    Account # 4024-850-0482
    Service Start Date 4/14/2018
    Service To 7/6/2018
    Meter Reading Start 1436.64
    Reading End 1454.86
  30. Click the Create Water Bill button
  31. Click the Save Water Bill button
  32. Close the browser and return to your programming environment

Items Maintenance in a Collection

Editting an Item

Editing an item consists of changing its value or one of its details. The IList interface doesn't provide its own method to do this. The reason is that it depends on the item and what you are trying to do. For example, if the collection is made of value types or strings, it is easy to change the value. If the collection is made of objects, then you must decide what value of a property you want to change, and this can be unpredictable. As done in formal database, the best steps to follow are:

Deleting a Value by its Index

If a value is not necessary in your collection, you can delete it. Probably the simplest way to delete a value is to specify its position in the list. To support this operation, both the System.Collections.IList and the System.Collections.Generic.IList interfaces are equipped with a method named RemoveAt. The syntax of the RemoveAt() method is the same for both interfaces and it is:

void RemoveAt(int index);

This method takes as argument the index of the value to be removed. If the index is valid, the method removes the item at that position. If the index is not valid, the compiler would throw an ArgumentOutOfRangeException exception. Here is an example:

public class Itemization<T> : IList<T>
{
    private int size;
    private T[] items;

    . . .
    
    // This method is used to delete an item based on its position
    public void RemoveAt(int index)
    {
        // First check that the user provided a positive index that
        // is lower than the current total number of items
        if ((index >= 0) && (index < size))
        {
            // If so, starting at that index, get the item ahead of
            // each position and assign it to the current item
            for (int i = index; i < size - 1; i++)
                items[i] = items[i + 1];

            // Since the last item is not useful anymore,
            // decrease the number of items
            size--;
        }
    }
}

If the item cannot be deleted for another reason, the compiler would throw a NotSupportedException exception.

Deleting an Item by its Value

The above method could deleting the wrong value or object if you provide the wrong index. An alternative is to first precisely define the value or the object you want to get rid of, and then hand the value or object to the compiler that would remove it. To support this approach, the System.Collections.IList interface is equipped with a method named Remove. Its syntax is:

bool Remove(T item);

This method takes as argument the value to be deleted. This means that the compiler will first look for the value in the list. If it finds that value, it removes it. If there is no value like that, nothing would happen. Here is an example of implementing this method:

public class Itemization<T> : IList<T>
{
    private int size;
    private T[] items;

    . . .
    
    // This method is used to delete an item
    public bool Remove(T value)
    {
        // First try to get the index of the item to find out if it exists
        int index = IndexOf(value);
        
        // If the item was found ...
        if (index >= 0)
        {
            // ... delete it, using its index
            RemoveAt(index);
            // Since the item has been removed, return true
            return true;
        }

        // If the item was not removed (for any reason), return false
        return false;
    }
}

Clearing a Collection

To let you remove all values or objects from a list, the IList<> interface is equipped with a method named Clear. Its syntax is:

void Clear();

Here is an example of implementing it:

public class Itemization<T> : IList<T>
{
    private int size;
    private T[] items;

    public Itemization()
    {
        size = 0;
        items = new T[10];
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        T[] values = new T[size];

        for (int i = 0; i < size; i++)
            values[i] = items[i];

        array = values;
    }

    public int Count
    {
        get { return size; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    private void CheckAndIncreaseIfNecessary()
    {
        if (size >= items.Length)
            Array.Resize<T>(ref items, items.Length + 5);
    }

    // The index property can be used to add an item to a collection,
    // or to retrieve an item from the collection
    public T this[int index]
    {
        get
        {
            return this.items[index];
        }
        set
        {
            this.items[index] = value;
        }
    }

    public void Add(T value)
    {
        // Check whether there is still room in the array.
        // If there is not enough room, then increase the capacity
        CheckAndIncreaseIfNecessary();

        // Add the item at the end
        this.items[this.size] = value;
        // Increase the current number of items
        this.size++;
    }

    public void Insert(int index, T value)
    {
        // If the index is the same as the current number of items
        // then call Add() and proceed as if we want to add a new item
        if (index >= size)
        {
            Add(value);
            return;
        }

        // If the index is positive but less than the current size,
        // then you need to insert the item inside the collection.
        if ((index >= 0) && (index < size))
        {
            // Firs, push each item one position up to create
            // an empty space
            for (int i = (size - 1); i > index - 1; i--)
                items[i + 1] = items[i];

            // Then put the new item in the indicated position
            items[index] = value;
            // Since the new item has been added, increase the 
            // current number of items
            size++;
        }
    }

    public bool Contains(T value)
    {
        for (int i = 0; i < Count; i++)
            if (items[i].Equals(value))
                return true;
        return false;
    }

    public int IndexOf(T value)
    {
        for (int i = 0; i < Count; i++)
            if (items[i].Equals(value))
                return i;
        return -1;
    }

    // This method is used to delete an item based on its position
    public void RemoveAt(int index)
    {
        // First check that the user provided a positive index that
        // is lower than the current total number of items
        if ((index >= 0) && (index < size))
        {
            // If so, starting at that index, get the item ahead of
            // each position and assign it to the current item
            for (int i = index; i < size - 1; i++)
                items[i] = items[i + 1];

            // Since the last item is not useful anymore,
            // decrease the number of items
            size--;
        }
    }

    // This method is used to delete an item
    public bool Remove(T value)
    {
        // First try to get the index of the item to find out if it exists
        int index = IndexOf(value);
        
        // If the item was found ...
        if (index >= 0)
        {
            // ... delete it, using its index
            RemoveAt(index);
            // Since the item has been removed, return true
            return true;
        }

        // If the item was not removed (for any reason), return false
        return false;
    }

    // This method is used to delete all items from a collection
    public void Clear()
    {
        // Visit each item in the collection and set it to the default value
        // (This is not really necessary)
        for (int i = 0; i < size; i++)
            items[i] = default(T);

        // Reset the number of items of the collection to 0
        size = 0;
    }

    public IEnumerator<T> GetEnumerator()
    {
        int counter = 0;

        while (counter < Count)
        {
            yield return items[counter];
            counter++;
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        int counter = 0;

        while (counter < Count)
        {
            yield return items[counter];
            counter++;
        }
    }
}

Practical LearningPractical Learning: Ending the Lesson


Previous Copyright © 2008-2019, FunctionX Next