guiomie
guiomie

Reputation: 5148

Are custom objects equal by value with List<>

I have the following classes:

 public class MyDocuments
    {
        public DateTime registeredDate;
        public string version;
        public List<Document> registeredDocuments;
    }

    public class Document
    {
        public string name;
        public List<File> registeredFiles;
    }

    public class File
    {
        public string name;
        public string content;
    }

I have an instance of MyDocuments which has several documents in List<Document> registeredDocument. I get a new List<Document> from the user.

How can I verify that the new object doesn't exist in the list? I want to compare by value not reference.

I'm thinking of using HashSet instead of List. Is this the proper approach?

Upvotes: 2

Views: 563

Answers (5)

user645280
user645280

Reputation:

Something like this at the top level, continued down at the sub-levels:

public class MyDocuments
{
    public DateTime registeredDate;
    public string version;
    public HashSet<Document> registeredDocuments;
    public override bool Equals(Object o)
    {
        if( !(o is MyDocuments) ) return false;
        MyDocuments that = (MyDocuments)o;
        if( !String.Equals(this.version, that.version) ) return false;
        if( this.registeredDocuments.Count != that.registeredDocuments.Count ) return false;
        // assuming registeredDate doesn't matter for equality...
        foreach( Document d in this.registeredDocuments )
            if( !that.registeredDocuments.Contains(d) )
                return false;
        return true;
    }
    public override int GetHashCode()
    {
        int ret = version.GetHashCode();
        foreach (Document d in this.registeredDocuments)
            ret ^= d.GetHashCode(); // xor isn't great, but better than nothing.
        return ret;
    }
}

Note: Caching could be useful for the HashCode values if the properties were change-aware.

Upvotes: 0

Jon
Jon

Reputation: 437604

How are equality comparisons performed?

Whenever the BCL classes want to perform an equality check between objects of some type T, they do so by calling one or both of the methods in some implementation of IEqualityComparer<T>. To get hold of such an implementation, the framework looks to EqualityComparer<T>.Default.

As mentioned in the documentation, this property produces an IEqualityComparer<T> like this:

The Default property checks whether type T implements the System.IEquatable<T> interface and, if so, returns an EqualityComparer<T> that uses that implementation. Otherwise, it returns an EqualityComparer<T> that uses the overrides of Object.Equals and Object.GetHashCode provided by T.

What are my options?

So, in general, to dictate how equality comparisons should be performed you can:

  1. Explicitly provide an implementation of IEqualityComparer<T> to the class or method that performs equality checks. This option is not very visible with List<T>, but many LINQ methods (such as Contains) do support it.
  2. Make your class implement IEquatable<T>. This will make EqualityComparer<T>.Default use this implementation, and is a good choice whenever there is an obvious "natural" way to compare objects of type T.
  3. Override object.GetHashCode and object.Equals without implementing IEqualityComparer<T>. However, this is simply an inferior version of #2 and AFAIK should always be avoided.

Which option to pick?

A good rule of thumb is: if there is an obvious and natural way to compare objects of class T, consider having it implement IEquatable<T>; this will make sure your comparison logic is used throughout the framework without any additional involvement. If there is no obvious candidate, or if you want to compare in a manner different than the default, implement your own IEqualityComparer<T> and pass the implementation as an argument to the class or method that needs to perform equality checks.

Upvotes: 2

Maarten
Maarten

Reputation: 22955

You should implement IEquatable<T>.

When you implement this interface on your custom object, any equality checks (e.g. Contains, IndexOf) are automatically done using your objects implementation.

Upvotes: 1

Bobson
Bobson

Reputation: 13706

You will need to implement the Equals() method, and probably GetHashCode() as well. See this answer for an example.

Upvotes: 1

override the object.Equals method.

here's an example straight from the documentation

public class Person
{
   private string idNumber;
   private string personName;

   public Person(string name, string id)
   {
      this.personName = name;
      this.idNumber = id;
   }

   public override bool Equals(Object obj)
   {
      Person personObj = obj as Person; 
      if (personObj == null)
         return false;
      else 
         return idNumber.Equals(personObj.idNumber);
   }

   public override int GetHashCode()
   {
      return this.idNumber.GetHashCode(); 
   }
}

the Equals method returns a bool which is whether or not obj is equal to this

Upvotes: 0

Related Questions