-2

I need help sorting a collection object. My Collection class provides functionality around an ArrayList which holds a list of Customer objects. I have added a Sort Method to the class. I am implementing IComparable in my Customer class and have written a CompareTo method. My Sort function fails and throws the error: System.ArgumentException: At least one object must implement IComparable.

My objective is to sort the customers by Lastname, Firstname, CustomerNo.

I've written up a little console app to reproduce this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp4Testing
{
    class Program
    {
        static void Main(string[] args)
        {
            Customers customers = new Customers();
            Customer customer = new Customer("C00066", "John", "Smith", "1234 Main St", "", "Boise", "ID", "53432", "US");
            customers.Add(customer);
            customer = new Customer("C00017", "Bob", "Jones", "1001 First Ave", "", "Detroit", "MI", "84772", "US");
            customers.Add(customer);
            customer = new Customer("C00024", "Susan", "Day", "PO Box 2509", "", "Dallas", "TX", "57212", "US");
            customers.Add(customer);
            customer = new Customer("C00009", "Bill", "Mason", "987 Washington Av", "", "Los Angeles", "CA", "90254", "US");
            customers.Add(customer);
            customer = new Customer("C00042", "Alice", "Jones", "1401 G St", "", "Atlanta", "GA", "65354", "US");
            customers.Add(customer);
            customer = new Customer("C00035", "Joan", "King", "879 Chestnut St", "", "Philadelphia", "PA", "22531", "US");
            customers.Add(customer);
            customer = new Customer("C00013", "John", "Smith", "67 Filmore Ave", "", "Chicago", "IL", "61535", "US");
            customers.Add(customer);
            Console.WriteLine("Customers in order as added:");
            foreach (Customer cust in customers)
            {
                Console.WriteLine(cust.AccountNo + ", " + cust.FirstName + " " + cust.LastName);

            }
            Console.WriteLine();

            customers.Sort();
            Console.WriteLine("Customers sorted by Lastname, FirstName, AccountNo:");
            foreach (Customer cust in customers)
            {
                Console.WriteLine(cust.AccountNo + ", " + cust.FirstName + " " + cust.LastName);
            }
            Console.ReadLine();
        }
    }
}

...and then my Customers and Customer classes:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace ConsoleApp4Testing
{
    public class Customers : IEnumerable
    {
        private ArrayList m_customerList;

        public Customers()
        {
            m_customerList = new ArrayList();
        }

        public Customer this[int index]
        {
            get { return (Customer)m_customerList[index]; }
            set
            {
                if (index > (m_customerList.Count - 1))
                { m_customerList.Add(value); }
                else
                { m_customerList[index] = value; }
            }
        }
        public Customer this[Guid custId]
        {
            get { return (Customer)m_customerList[indexof(custId)]; }
        }
        public int indexof(Guid custId)
        {
            int i = 0;
            foreach (Customer cst in this.m_customerList)
            {
                if (cst.ID == custId) { break; }
                i++;
            }
            if (i >= this.count)
            { return -1; }
            else
            { return i; }
        }
        public bool Exists(Guid custId)
        {
            int i = 0;
            foreach (Customer cst in this.m_customerList)
            {
                if (cst.ID == custId) { break; }
                i++;
            }
            if (i >= this.count)
            { return false; }
            else
            { return true; }
        }
        public void Add(Customer customer)
        {
            //if (this.indexof(customer.ID) < 0)  //Don't add the customer if it already exists
            //{ 
                this.m_customerList.Add(customer);
            //}
        }
        public void Sort()
        {
            try { 
                this.m_customerList.Sort(); 
            }
            catch (Exception ex)
            { System.Diagnostics.Debug.Print(ex.ToString()); }
        }
        public int count
        {
            get { return m_customerList.Count; }
        }
        // IEnumerable Interface Implementation:
        //   Declaration of the GetEnumerator() method 
        //   required by IEnumerable
        public IEnumerator GetEnumerator()
        {
            return new customerEnumerator(this);
        }

        // Inner class implements IEnumerator interface:
        private class customerEnumerator : IEnumerator
        {
            private int position = -1;
            private Customers cstmrs;

            public customerEnumerator(Customers cstmrs)
            {
                this.cstmrs = cstmrs;
            }

            // Declare the MoveNext method required by IEnumerator:
            public bool MoveNext()
            {
                if (position < cstmrs.m_customerList.Count - 1)
                {
                    position++;
                    return true;
                }
                else
                {
                    return false;
                }
            }

            // Declare the Reset method required by IEnumerator:
            public void Reset()
            {
                position = -1;
            }

            // Declare the Current property required by IEnumerator:
            public object Current
            {
                get
                {
                    return cstmrs.m_customerList[position];
                }
            }
        }
    }

    public class Customer : IComparable<Customer>
    {
        private Guid _id;
        private string _acctNo;
        private string _frstNm;
        private string _lastNm;
        private string _addr1;
        private string _addr2;
        private string _city;
        private string _st;
        private string _postal;
        private string _country;

        public Customer(Guid id)
        {
            this._id = id;
        }
        public Customer(string acctNo, string frstNm, string lastNm, string addr1, string addr2, string city, string state, string postal, string country)
        {
            this._acctNo = acctNo;
            this._frstNm = frstNm;
            this._lastNm = lastNm;
            this._addr1 = addr1;
            this._addr2 = addr2;
            this._city = city;
            this._st = state;
            this._postal = postal;
            this._country = country;
        }
        public int CompareTo(Customer c)
        {
            int compare;
            compare = String.Compare(this.LastName, c.LastName, true);
            if (compare == 0)
            {
                compare = this.FirstName.CompareTo(c.FirstName);
                if (compare == 0)
                {
                    compare = this.AccountNo.CompareTo(c.AccountNo);
                }
            }
            return compare;
        }
        public Guid ID 
        { 
            get { return _id; }
            set { this._id = value; }
        }
        public string AccountNo
        {
            get { return _acctNo; }
            set { this._acctNo = value; }
        }
        public string FirstName
        {
            get { return _frstNm; }
            set { this._frstNm = value; }
        }
        public string LastName
        {
            get { return _lastNm; }
            set { this._lastNm = value; }
        }
        public string Address1
        {
            get { return _addr1; }
            set { this._addr1 = value; }
        }
        public string Address2
        {
            get { return _addr2; }
            set { this._addr2 = value; }
        }
        public string City
        {
            get { return _city; }
            set { this._city = value; }
        }
        public string State
        {
            get { return _st; }
            set { this._st = value; }
        }
        public string Postal
        {
            get { return _postal; }
            set { this._postal = value; }
        }
        public string Country
        {
            get { return _country; }
            set { this._country = value; }
        }
    }
}
JMLilly
  • 31
  • 3
  • By the way isn't better idea to sort using linq OrderBy and ThenBy? – MistyK Mar 06 '15 at 14:10
  • Why did you implement your own collection instead of using a List or any other generic collection? The code you wrote is already available either by List itself or as a LINQ extension method. – Panagiotis Kanavos Mar 06 '15 at 14:13
  • possible duplicate of [Sorting a collection of objects](http://stackoverflow.com/questions/1206073/sorting-a-collection-of-objects) – ivan_pozdeev Mar 07 '15 at 01:59

3 Answers3

2

just use the standard generic list and Linq to sort

        List<Customer> customers = new List<Customer>();

        foreach(Customer cust in customers.OrderBy(c=>c.LastName).ThenBy(c=>c.FirstName).ThenBy(c=>c.CustomerNo))
        {
        }
Ewan
  • 1,261
  • 1
  • 14
  • 25
1

I suspect the non-generic ArrayList is looking for the non-generic IComparable, but you've implemented the generic IComparable<T> instead. Try implementing the non-generic one:

public class Customer : IComparable
{
    // ...

    public int CompareTo(object obj) {
        if (obj == null) return 1;

        Customer c = obj as Customer;
        if (c == null) 
           throw new ArgumentException("Object is not a Customer");

        int compare;
        compare = String.Compare(this.LastName, c.LastName, true);
        if (compare == 0)
        {
            compare = this.FirstName.CompareTo(c.FirstName);
            if (compare == 0)
            {
                compare = this.AccountNo.CompareTo(c.AccountNo);
            }
        }
        return compare;
    }
}

Conversely, you might use a generic collection instead of the non-generic ArrayList. Then sorting can even be more simply done without implementing IComparable<T> because your Customers implementation can internally just use the .OrderBy() extensions on the generic collections.

David
  • 208,112
  • 36
  • 198
  • 279
0

Try changing your m_customerList declaration to this:

List<Customer> m_customerList;

You need to specify what objects the List is going to hold in order to properly call your Sort method. You have to use List instead of ArrayList to specify the object type.

Patrick Smith
  • 261
  • 1
  • 6