2

I have the following class with the 2 equal methods as documented by MSDN.

public class Book
{

public string bookTitle {get; private set;}
public IReadOnlyCollection<Author> authors {get; private set;}
public string ISBN {get; private set;}
public int numberofpages {get; private set; }
public string Genre {get; private set; }

public Book(string bookTitle, IReadOnlyCollection<Author> authors, string ISBN, int numberofpages, string genre)
{
    if(string.IsNullOrWhiteSpace(bookTitle)){
        throw new ArgumentNullException("Book Must Have Title!");
    }
    this.bookTitle = bookTitle;

    if(authors.Count < 0){
        throw new ArgumentNullException("You must provide at least one author!");
    }
    this.authors = new ReadOnlyCollection<Author>(new List<Author>(authors));

    if(String.IsNullOrWhiteSpace(ISBN)){
        throw new ArgumentNullException("A Book Has to have an ISBN number. Check online or the back cover");
    }
    this.ISBN = ISBN;
    if(numberofpages <= 0){
        throw new ArgumentNullException("A Book has more than one page!");
    }
    this.numberofpages = numberofpages;
    if(String.IsNullOrWhiteSpace(genre)){
        throw new ArgumentNullException("A Book has a genre. Find it and input it");
    }
    this.Genre = genre;
}


public override bool Equals(Object obj)
{
    if (obj == null)
    {
        return false;
    }

    Book p = obj as Book;
    if ((System.Object)p == null)
    {
        return false;
    }
    return (bookTitle == p.bookTitle) && (authors == p.authors) && (numberofpages == p.numberofpages) && (ISBN == p.ISBN) && (Genre == p.Genre);
}

public bool Equals(Book p)
{
    if ((object)p == null)
    {
        return false;
    }

    return (bookTitle == p.bookTitle) && (authors == p.authors) && (numberofpages == p.numberofpages) && (ISBN == p.ISBN) && (Genre == p.Genre);
}


   public class Author
   {
     public int ID {get; private set;}
     public string firstname {get; private set;}
     public string lastname {get; private set;}

     public(int id, string firstname, string lastname)
     {
        this.ID = id;
        this.firstname = firstname;
        this.lastname = lastname;
     }

     //Rest of code here: just toString method

}

My Problem:

Both these methods will evaluate to false because I'm creating a new List before I assign my authors in the constructor:

this.authors = new ReadOnlyCollection<Author>(new List<Author>(authors)); 

I did this so that a user cannot make changes to the ReadOnlyCollection outside of the class. Any changes made would be on a copy of the collection. With that in mind, how do I get my Equals to method to work properly, given that I create a new list?

  • You have to compare the Authors List items. Sort and compare the author at each index until they are not equal or you reach the end of list. Further, both Book and Author should implement IEquatable to prevent issues and maintain sanity. – Max Sorin Mar 09 '16 at 21:05
  • @MaxSorin - Could you show me how? –  Mar 09 '16 at 21:09
  • Answer provided with code and interfaces implemented – Max Sorin Mar 09 '16 at 21:34

2 Answers2

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

namespace TestArea.Other
{
    public class Author : IComparable<Author>
    {
        public string Name { get; set; }

        public int CompareTo(Author other) => this.Name.CompareTo(other.Name);
    }

    public class Authors :  ReadOnlyCollection<Author>, IEquatable<Authors>
    {
        public Authors(IList<Author> list) : base(list)
        {

        }

        public bool Equals(Authors other)
        {
            //reference equal 
            if (other == this)
            {
                return true;
            }

            //No need to iterate over authors
            if (other == null || other.Count != this.Count)
            {
                return false;
            }

            var thisSorted = this.ToArray();
            var otherSorted = other.ToArray();
            Array.Sort(thisSorted);
            Array.Sort(otherSorted );

            for (int i = 0; i < thisSorted.Length; i++)
            {
                if (thisSorted[i].CompareTo(otherSorted[i]) != 0)
                {
                    return false;
                }
            }

            return true;
        }
    }

    public class Book : IEquatable<Book>
    {

        public string bookTitle { get; private set; }
        public Authors authors { get; private set; }
        public string ISBN { get; private set; }
        public int numberofpages { get; private set; }
        public string Genre { get; private set; }

        //Made Authors parameter as simplified as it could be
        public Book(string bookTitle, IEnumerable<Author> authors, string ISBN, int numberofpages, string genre)
        {
            var authorList = authors.ToList();
            if (string.IsNullOrWhiteSpace(bookTitle))
            {
                throw new ArgumentNullException("Book Must Have Title!");
            }
            this.bookTitle = bookTitle;

            if (authorList.Count() < 0)
            {
                throw new ArgumentNullException("You must provide at least one author!");
            }
            this.authors = new Authors(new List<Author>(authorList));

            if (String.IsNullOrWhiteSpace(ISBN))
            {
                throw new ArgumentNullException("A Book Has to have an ISBN number. Check online or the back cover");
            }
            this.ISBN = ISBN;
            if (numberofpages <= 0)
            {
                throw new ArgumentNullException("A Book has more than one page!");
            }
            this.numberofpages = numberofpages;
            if (String.IsNullOrWhiteSpace(genre))
            {
                throw new ArgumentNullException("A Book has a genre. Find it and input it");
            }
            this.Genre = genre;
        }


        public override bool Equals(Object obj)
        {
            if (obj == null)
            {
                return false;
            }

            Book p = obj as Book;
            if ((System.Object) p == null)
            {
                return false;
            }
            return (bookTitle == p.bookTitle) && (authors.Equals( p.authors)) && (numberofpages == p.numberofpages) &&
                   (ISBN == p.ISBN) && (Genre == p.Genre);
        }

        public bool Equals(Book p)
        {
            if ((object) p == null)
            {
                return false;
            }

            return (bookTitle == p.bookTitle) && (authors.Equals( p.authors)) && (numberofpages == p.numberofpages) &&
                   (ISBN == p.ISBN) && (Genre == p.Genre);
        }
    }

}

Generic IEnumerable Equality method:

 public static class IEnumerableExtensions
    {
        public static bool EqualTo<T>(this IEnumerable<T> enumerable, IEnumerable<T> other)
        {
            //reference equal 
            if (other == enumerable)
            {
                return true;
            }

            if (other == null)
            {
                return false;
            }

            var enumerableSorted = enumerable.ToArray();
            var otherSorted = other.ToArray();

            //No need to iterate over items if lengths are not equal
            if (otherSorted.Length != enumerableSorted.Length)
            {
                return false;
            }

            Array.Sort(enumerableSorted);
            Array.Sort(otherSorted);

            return !enumerableSorted.Where((t, i) => t.Equals(otherSorted[i])).Any();
        }
    }

Usage:

public override bool Equals(Object obj)
{
    if (obj == null)
    {
        return false;
    }

    Book otherBook = obj as Book;
    if ((System.Object) otherBook == null)
    {
        return false;
    }

    return (bookTitle == otherBook.bookTitle) && 
        otherBook.authors.EqualTo(this.authors) && 
        (numberofpages == otherBook.numberofpages) &&
        (ISBN == otherBook.ISBN) && 
        (Genre == otherBook.Genre);
}
Max Sorin
  • 1,042
  • 6
  • 14
  • Many thanks for this, however, Authors isn't as complex as this. It's just a class with int author ID, string authorFirstname, string authorLastName. –  Mar 09 '16 at 21:51
  • `Authors` just allowed me to compare the lists to each other in a single statement. You could create your own method for comparing items in `ReadOnlyCollections` – Max Sorin Mar 09 '16 at 21:53
  • I updated my code to include my Authors class, please have a look, if you could alter your code, so that I can understand what to do. Scroll Down it's at the bottom –  Mar 09 '16 at 21:56
  • Bare with me please, I'm learning, could you show me how to create my own method for compairing items in the ReadOnlyCollections, please? –  Mar 09 '16 at 23:50
  • 1
    why isn't this enough for the equals method to work? https://msdn.microsoft.com/en-us/library/ms173147%28v=vs.90%29.aspx –  Mar 09 '16 at 23:58
  • How does one compare a collection to another collection? In your case you would want to find out if the two books being compared were written by the exact same set of authors. So you would want to make sure that each item in `book.Authors` has an equal counterpart in the `otherBook.Authors` and vice versa. A quick way to do that is to compare that the collection lengths are equal and that once they are sorted the same author appears in both collections at the same index. – Max Sorin Mar 10 '16 at 13:10
0

Your Book class could/should be implementing IComparable and IEquatable. You might also want to keep your collection sorted Sort ObservableCollection<string> C#. Without a little more info, hard to recommend a rock-solid answer, but if you look into the links/suggestions I made, you might make good progress on your project.

Community
  • 1
  • 1
Kory Gill
  • 6,993
  • 1
  • 25
  • 33