Click here to Skip to main content
15,885,998 members
Articles / Programming Languages / C#

A Study In Equality

Rate me:
Please Sign up or sign in to vote.
4.22/5 (12 votes)
26 Dec 2007CPOL6 min read 36.3K   105   22  
Understanding value and ref types for equality tests and as keys in collections
using System;
using System.Collections.Generic;

// A program to test and demonstrate the difference between reference and value types when used in comparisons
// and as keys to dictionaries.

namespace refvaluetests
{
	public struct AStruct
	{
		private readonly int i;

		public int I
		{
			get { return i; }
		}

		public AStruct(int i)
		{
			this.i = i;
		}
	}

	public struct AStructWithOperatorEquals
	{
		private readonly int i;

		public AStructWithOperatorEquals(int i)
		{
			this.i = i;
		}

		public static bool operator ==(AStructWithOperatorEquals s1, AStructWithOperatorEquals s2)
		{
			bool ret = false;

			if (((object)s1 != null) && ((object)s2 != null))
			{
				ret = s1.i == s2.i;
			}

			return ret;
		}

		/// <summary>
		/// If one is defined, the other is required.
		/// </summary>
		public static bool operator !=(AStructWithOperatorEquals s1, AStructWithOperatorEquals s2)
		{
			return !(s1 == s2);
		}

		/// <summary>
		/// Also this is required!
		/// </summary>
		public override bool Equals(object obj)
		{
			bool ret = false;

			if (!(obj is AStructWithOperatorEquals))
			{
				ret = false;
			}
			else
			{
				ret = i == ((AStructWithOperatorEquals)obj).i;
			}

			return ret;
		}

		/// <summary>
		/// Avoid the compiler warning by implementing this method.
		/// </summary>
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}
	}

	public class AClass
	{
		private readonly int i;

		public int I
		{
			get { return i; }
		}

		private AClass() { }

		public AClass(int i)
		{
			this.i = i;
		}
	}

	public class AnEqualsClass
	{
		public readonly int i;

		private AnEqualsClass() { }

		public AnEqualsClass(int i)
		{
			this.i = i;
		}

		public override bool Equals(object obj)
		{
			bool ret = false;
			AnEqualsClass s = obj as AnEqualsClass;

			if (s == null)
			{
				ret = false;
			}
			else
			{
				ret = i == s.i;
			}

			return ret;
		}

		/// <summary>
		/// Avoid the compiler warning by implementing this method.
		/// We need this method for Equals to work with generics like lists and Dictionaries.
		/// </summary>
		public override int GetHashCode()
		{
			// This is very important!
			// We return the hash code of our field, not the base algorithm.
			// The base algorithm returns different values for different instances.
			// RIGHT:
			return i.GetHashCode();
			// WRONG:
			// return base.GetHashCode();
		}
	}

	public class AnOperatorEqualClass
	{
		private readonly int i;

		public int I
		{
			get { return i; }
		}

		private AnOperatorEqualClass() { }

		public AnOperatorEqualClass(int i)
		{
			this.i = i;
		}

		public static bool operator ==(AnOperatorEqualClass s1, AnOperatorEqualClass s2)
		{
			bool ret = false;

			if (((object)s1 != null) && ((object)s2 != null))
			{
				ret = s1.i == s2.i;
			}

			return ret;
		}

		/// <summary>
		/// If one is defined, the other is required.
		/// </summary>
		public static bool operator !=(AnOperatorEqualClass s1, AnOperatorEqualClass s2)
		{
			return !(s1 == s2);
		}

		/// <summary>
		/// Also this is required!		 
		/// </summary>
		public override bool Equals(object obj)
		{
			bool ret = false;
			AnOperatorEqualClass s = obj as AnOperatorEqualClass;

			if (s==null)
			{
				ret = false;
			}
			else
			{
				ret = i == s.i;
			}

			return ret;
		}

		/// <summary>
		/// Avoid the compiler warning by implementing this method.
		/// </summary>
		public override int GetHashCode()
		{
			// This is very important!
			// We return the hash code of our field, not the base algorithm.
			// The base algorithm returns different values for different instances.
			return i.GetHashCode();
		}
	}

	class Program
	{
		static void Main(string[] args)
		{
			CompareStructs();
			CompareStructsWithOperatorEquals();
			CompareClasses();
			CompareEqualsClasses();
			CompareOperatorEqualClasses();
			IndexStructs();
			IndexClasses();
			IndexEqualsClasses();
			DictionaryStruct();
			DictionaryClasses();
			DictionaryEqualsClasses();
			Console.WriteLine("\r\n");
		}

		static void CompareStructs()
		{
			Console.WriteLine("CompareStructs:");
			AStruct s1 = new AStruct(10);
			AStruct s2 = new AStruct(10);
			Console.WriteLine("AStruct.Equals(AStruct) ? " + ((s1.Equals(s2)) ? "Yes" : "No"));
			// operator == cannot be applied to operands AStruct because operator == is not implemented.
			// Console.WriteLine("s1 == s2 ? " + ((s1 == s2) ? "Yes" : "No"));
		}

		static void CompareStructsWithOperatorEquals()
		{
			Console.WriteLine("\r\nCompareStructsWithOperatorEquals:");
			AStructWithOperatorEquals s1 = new AStructWithOperatorEquals(10);
			AStructWithOperatorEquals s2 = new AStructWithOperatorEquals(10);
			Console.WriteLine("s1 == s2 ? " + ((s1 == s2) ? "Yes" : "No"));
		}

		static void CompareClasses()
		{
			Console.WriteLine("\r\nCompareClasses:");
			AClass s1 = new AClass(10);
			AClass s2 = new AClass(10);
			Console.WriteLine("AClass.Equals(AClass) ? " + ((s1.Equals(s2)) ? "Yes" : "No"));
			Console.WriteLine("s1 == s2 ? " + ((s1 == s2) ? "Yes" : "No"));
		}

		static void CompareEqualsClasses()
		{
			Console.WriteLine("\r\nCompareEqualsClasses:");
			AnEqualsClass s1 = new AnEqualsClass(10);
			AnEqualsClass s2 = new AnEqualsClass(10);
			Console.WriteLine("AnEqualsClass.Equals(AnEqualsClass) ? " + ((s1.Equals(s2)) ? "Yes" : "No"));
			Console.WriteLine("s1 == s2 ? " + ((s1 == s2) ? "Yes" : "No"));
		}

		static void CompareOperatorEqualClasses()
		{
			Console.WriteLine("\r\nCompareOperatorEqualClasses:");
			AnOperatorEqualClass s1 = new AnOperatorEqualClass(10);
			AnOperatorEqualClass s2 = new AnOperatorEqualClass(10);
			Console.WriteLine("AnOperatorEqualsClass.Equals(AnOperatorEqualsClass) ? " + ((s1.Equals(s2)) ? "Yes" : "No"));
			Console.WriteLine("s1 == s2 ? " + ((s1 == s2) ? "Yes" : "No"));
		}

		static void IndexStructs()
		{
			Console.WriteLine("\r\nIndexStructs:");
			List<AStruct> list = new List<AStruct>();
			AStruct s1 = new AStruct(10);
			list.Add(s1);
			AStruct s2 = new AStruct(10);
			Console.WriteLine("List contains s2 : " + list.Contains(s2));
		}

		static void IndexClasses()
		{
			Console.WriteLine("\r\nIndexClasses:");
			List<AClass> list = new List<AClass>();
			AClass s1 = new AClass(10);
			list.Add(s1);
			AClass s2 = new AClass(10);
			Console.WriteLine("List contains s2 : " + list.Contains(s2));
		}

		static void IndexEqualsClasses()
		{
			Console.WriteLine("\r\nIndexEqualsClasses:");
			List<AnEqualsClass> list = new List<AnEqualsClass>();
			AnEqualsClass s1 = new AnEqualsClass(10);
			list.Add(s1);
			AnEqualsClass s2 = new AnEqualsClass(10);
			Console.WriteLine("List contains s2 : " + list.Contains(s2));
		}

		static void DictionaryStruct()
		{
			Console.WriteLine("\r\nDictionaryStruct:");
			Dictionary<AStruct, int> dict = new Dictionary<AStruct, int>();
			AStruct s1 = new AStruct(10);
			dict[s1] = 1;
			AStruct s2 = new AStruct(10);
			Console.WriteLine("Dictionary contains s2 : " + dict.ContainsKey(s2));
		}

		static void DictionaryClasses()
		{
			Console.WriteLine("\r\nDictionaryClasses:");
			Dictionary<AClass, int> dict = new Dictionary<AClass, int>();
			AClass s1 = new AClass(10);
			dict[s1] = 1;
			AClass s2 = new AClass(10);
			Console.WriteLine("Dictionary contains s2 : " + dict.ContainsKey(s2));
		}

		static void DictionaryEqualsClasses()
		{
			Console.WriteLine("\r\nDictionaryEqualsClasses:");
			Dictionary<AnEqualsClass, int> dict = new Dictionary<AnEqualsClass, int>();
			AnEqualsClass s1 = new AnEqualsClass(10);
			dict[s1] = 1;
			AnEqualsClass s2 = new AnEqualsClass(10);
			Console.WriteLine("Dictionary contains s2 : " + dict.ContainsKey(s2));
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions