Algorithms, C#, Uncategorized

Bit manipulation to the rescue in C#. Reducing Space/Time complexity using Bit Manipulation

Why

Bit manipulation is fun and still  relevant in the realm of C#. You can do things in a way that can increase performance substantially or simplify an algorithm in hand.

Bit manipulation also in certain scenarios helps to save memory, take for example if you want to store multiple flags (True/False values) the most obvious solution would be to use a list of bool types. This may seem to be OK but takes 1 byte per flag. If you are working with 30 flags in hand, you can use an Int32 type in .Net and use its bit values to store 1 for truthy values and 0 for falsy values (often referred as using  bits of an Integer to represent a Set)

So although it’s not imperative to know Bit Manipulation but it can really come to rescue in some corner cases as we plan to see in this blog post.

 

Bitwise Operators

Before diving deep into scenarios where Bit manipulation can come to rescue let’s look into various Bitwise operators so that we all start on the same page:

 NOTE: I am using 8 bits to explain the concepts.

The .Net int type is a  32 bit integer, but the concepts would remain the same 

Bitwise Compliment
/*Bitwise Compliment
             * 1  = 00000001
             * ~1 = 11111110 (In 2's compliment representation this is -2) 
             * ------Explanation of -2:---------
             *  2 = 00000010
             * -2 = 11111101 +1 = 11111110
             */
            Console.WriteLine(~1);
 
 
Bitwise And / Set Intersection

            /*Bitwise And 
             * 1  = 00000001
             * 2  = 00000010
             * 1&2= 00000000 = 0
             */
            Console.WriteLine(1 & 2);
 
Bitwise Or / Set Union
 
            //Bitwise Or
            //1  = 00000001
            //2  = 00000010
            //1|2= 00000011 = 3
            Console.WriteLine(1 | 2);
 
 
Bitwise XOR
            /*
               Here's how XOR works:
             * If both operands have a bit set i.e. 1, the result does not 
             * have that bit set.
             * If neither has a bit set, the result still does not have
             * the bit set. 
             * But if one has the bit set, the result is set.
             * XOR is used for checking bit parity
             */
 
 
            //Bitwise XOR
            //1  = 00000001
            //2  = 00000010
            //1&2= 00000011 = 3
            Console.WriteLine(1 ^ 2);
 
Bitwise Shift Operators
One common use of shifting is to access a particular bit, for example, 
1 << a is a binary number with bit a set and the others clear(bits are 
always counted from the least significant bit, which is numbered 0)
 
Bitwise Right Shift

            //Bitwise Right Shift. Right shift is equivalent to division by 2
            //1   = 00000001
            //1>>2= 00000000 = 0
            Console.WriteLine(1 >> 2);
 
Bitwise Left Shift
 
            //Bitwise Left Shift . Left shift is equivalent to multiplying by 2
            //1   = 00000001
            //1<<2= 00000100 = 4
            Console.WriteLine(1 << 2);
 
Flag Enums
 
            // Bitwise OR operator is used for adding a flag to the Enum
            States states = States.AL | States.TN;
 
            CheckStates(states);
 
            states |= States.MN;
 
            CheckStates(states);
 
            states |= States.WI;
 
            CheckStates(states);
 
            // Bitwise ^ operator is used for removing a flag from the Enum
            states ^= States.AL;
 
            CheckStates(states);
        }
 
         private static void CheckStates(States states)
        {
            switch (states)
            {
 
                case States.AL:
                    {
                        Console.WriteLine("AL");
 
                    }
                    break;
                case States.AL | States.TN:
                    {
                        Console.WriteLine("AL, TN");
 
                    }
                    break;
 
                case States.AL | States.MN:
                    {
                        Console.WriteLine("AL, MN");
 
                    }
                    break;
            }
        }

Set Subtraction

// Set Subtraction
Console.WriteLine(a & ~b);

Set subtraction can be easily understood by using a diagram:

Set Subtraction

Set Subtraction

 

Set Negation

// Set Negation
           Console.WriteLine(ALLBITS ^ a);

Here ALLBITS is an integer value, wherein all bits are 1

 

Set Bit

In order to set a bit, use the following:

// Set Bit
            a |= 1 << shift;

Clear Bit

// Clear Bit
           a &= ~(1 << shift);

 

Now let us look into a few problems wherein we can

leverage Bit manipulations to Reduce Complexity of

an Algorithm/ Reduce space requirements of a program.

 

Scenario 1: Reducing Time Complexity

Given two Integers, find whether they are of opposite signs or not.

Here is the obvious solution:

 

public static bool AreIntegersOfOppositeSigns(int a, int b)
       {
           if (a > 0 && b < 0)
           {
               return true;
           }
 
           if (a < 0 && b > 0)
           {
               return true;
           }
 
           return false;
       }

And here is the solution which uses Bit manipulation:

public static bool AreIntegersOfOppositeSignsUsingBitManipulation(int a, int b)
       {
           return (a^b) < 0;
       }

Did you notice the no. Of lines getting reduced? But it’s not just the lines getting reduced, it’s the complexity/

branching which got reduced here.

First solution has 3 branches/paths and 9 operators whereas the 2nd solution has no branching, additionally just 3 operators.

 

Scenario 2: Reducing Space Complexity

Given a String, determine if all of its characters are Unique

Here is one of the solutions wherein we use a Char Array of size 256 (assuming dealing with ASCII) as a HashTable

to figure out if there are any duplicates:

 

public static bool AreUniqueChars(String str)
     {
         var chars = new bool[256];
         for (int i = 0; i < str.Length; i++)
         {
             int val = str[i];
             if (chars[val]) return false;
             chars[val] = true;
         }
         return true;
     }


public static bool AreUniqueCharsUsingBitManipulation(String str)
   {
       int checker = 0;
       for (int i = 0; i < str.Length; ++i)
       {
           int val = str[i] - 'a';
           if ((checker & (1 << val)) > 0) return false;
           checker |= (1 << val);
       }
       return true;
   }

In this scenario we got rid of the Character array that we were using as a HashTable to detect duplicates.

Final Words

We saw two specific scenarios to reduce Space and Time complexity of an Algorithm. I hope you got the idea of the

power of Bit Manipulation and how can we use it to write performant and efficient code.

Standard
C#, MultiThreading

Ways to implement MultiThreading in C#

I have been using Threads in C# from quite a while now and it would not be wrong to say that I always mix up APIs in C# which allow to create threads/ use thread pools for that matter.

There are literally many ways to go about Threading in C# and I finally decided to jot down all ways I know so that I don’t end up mixing them again.

Here are the ways with code samples:

         1
         // Passing the method name to be executed in the ctor directly without using ThreadStart delegate
         var thread1 = new Thread(DoSomeWork);
         thread1.Start();
 
         2
         // Passing the ThreadStart delegate  which points to a method to be executed
         var threadStart = new ThreadStart(DoSomeWork);
         var thread2 = new Thread(threadStart);
         thread2.Start();
 
          3
         // Passing the ParametrizedThreadStart delegate  which points to the method to be executed
         var parametrizedThreadStart = new ParameterizedThreadStart(DoSomeWorkWithParameter);
         var thread3 = new Thread(parametrizedThreadStart);
         thread3.Start(2);
 
         4
         // Passing a Lambda expression in the Thread class constructor and subsequently calling the Start method
         var thread4 = new Thread(() =>
         {
             int x = 5;
             for (int i = 0; i < x; i++)
             {
                 Console.WriteLine(i);
             }
         });
 
         thread4.Start();
 
         5
         // Leveraging ThreadPools, call ThreadPool.QueueUserWorkItem passing in the method name to be executed
         ThreadPool.QueueUserWorkItem(DoSomeWorkWithParameter);
         ThreadPool.QueueUserWorkItem(DoSomeWorkWithParameter, 4);
 
            6
         // Using TPL (Task Parallel Library). Create a Task<T>, where T is the return type of the method to be executed.
         Task<string> task = Task.Factory.StartNew<string>(DoSomeStringWork);
         var result = task.Result;
 
           7
        // Instantiating Task class directly
            var task = new Task(
                () =>
                {
                    Console.WriteLine("Worker thread!");
                    Thread.Sleep(1000);
                });
 
            task.Start();

 8
// Using Asynchronous Delegates, also known as the APM pattern 
Func<stringstring> work = DoSomeStringWork;
IAsyncResult res = work.BeginInvoke(“Hello”nullnull);
string result1 = work.EndInvoke(res);
}

Utility Methods:

static void DoSomeWorkWithParameter(object a)
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine(“Do some work invoked, Thread Id:{0}, i);
}

}

static void DoSomeWork()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine(“Do some work invoked, Thread Id:{0}, i);
}

}

static string DoSomeStringWork()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine(“Do some work invoked, Thread Id:{0}, i);
}
return “a”;
}

static string DoSomeStringWork(string a)
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine(“Do some work invoked, Thread Id:{0}, i);
}
return “a”;
}

Standard
C#

How to go about copying an object in C#

Whenever there is a need for copying/cloning an object in C# the first thing that comes to mind is the ICloneable Interface which has only one method i.e. Clone which looks like this:

private class Student : ICloneable
{
 
    public object Clone()
    {
        throw new NotImplementedException();
    }
}

We can go ahead and override this Clone method and send back an object, but there is fundamental issue.

The problem roots in the fact that when you say copy, it’s ambiguous until you say it’s a  Shallow copy or a Deep copy.

Shallow Copy:

You copy the least i.e. You just copy the non-static fields of the object but then if there’s a reference type you don’t copy it’s properties recursively. You just copy the reference.

Deep Copy:

You copy all reference types as part of the object recursively, you just don’t copy the references alone. You create a new instance of the reference type and copy the data from the reference in hand.

 

Now, because ICloneable’s clone method doesn’t explicitly state that it’s going to give a Shallow copy or a Deep copy So do not implement the ICloneable Interface instead have your own clone method or copy method and ensure you clearly document that you are returning a shallow or a deep copy

Sample Code showing Implementation of Shallow & Deep copy:

class Employee
   {
       public Employee(string firstName, string lastName, Address address)
       {
           this.FirstName = firstName;
           this.LastName = lastName;
           this.Address = address;
       }
 
       public string FirstName { getprivate set; }
 
       public string LastName { getprivate set; }
 
       public Address Address { getprivate set; }
 
       public Employee ShallowCopy()
       {
           return (Employeethis.MemberwiseClone();
       }
 
       public Employee DeepCopy()
       {
           var copiedEmployee = (Employeethis.MemberwiseClone();
           copiedEmployee.Address = new Address(copiedEmployee.Address.HouseNo, copiedEmployee.Address.Street, copiedEmployee.Address.State, copiedEmployee.Address.PinCode);
           return copiedEmployee;
       }
   }



Let us understand the code now.

Shallow Copy:

For shallow copy all we have to do is call the protected method in Object class i.e. MemberWiseClone.

Now what MemberWiseClone does is, it creates a new object and then copies all the non-static fields. Also it makes sure that it copies all the value types bit-by-bit and copies the references of the reference type variables.

So if you do a shallow copy and then I change the Address type variable inside the employee object, it also changes the address object in the shallow copied object as they both are referring to the same Address object.

Deep Copy:

For deep copy you can first get a shallow copy by calling the memberwiseclone method and then create new instances of all reference types inside the class by newing them up and populating them using the data from the reference you already have in hand.

 

Final Words..

Stop using ICloneable, it is broken and would not help your consumers understand what it is actually doing.

Using MemberwiseClone method is safe for shallow copy but remember to recursively instantiate reference type variables if what you want is a deep copy.

Standard
C#, Design Patterns

Difference between Factory Method and Abstract Factory design patterns

First of all I will put both these patterns in context and describe their intent as in the GOF book:

 

Factory Method:

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Abstract Factory:

Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

 

Points to note:

  • Abstract factory pattern adds a layer of abstraction to the factory method pattern. The type of factory is not known to the client at compile time, this information is passed to the client at runtime (How it is passed is again dependent on the system, you may store this information in configuration files and the client can read it on execution).
  • While implementing Abstract factory pattern, the factory classes can have multiple factory methods.
  • In Abstract factory, a factory is capable of creating more than one type of product (Similar products are grouped together in a factory)

 

Sample implementation of factory method pattern

 

Let’s see the class diagram first:

class diagram

 

ProductFactory.cs

    1 // ———————————————————————–

    2 // <copyright file=”ProductFactory.cs” company=””>

    3 // TODO: Update copyright text.

    4 // </copyright>

    5 // ———————————————————————–

    6

    7 namespace FactoryMethod

    8 {

    9     using System;

   10     using System.Collections.Generic;

   11     using System.Linq;

   12     using System.Text;

   13

   14     /// <summary>

   15     /// TODO: Update summary.

   16     /// </summary>

   17     public abstract class ProductFactory

   18     {

   19         /// <summary>

   20         /// </summary>

   21         /// <returns>

   22         /// </returns>

   23         public abstract Product CreateProductInstance();

   24     }

   25 }

   26

ProductAFactory.cs

    1 // ———————————————————————–

    2 // <copyright file=”ProductAFactory.cs” company=””>

    3 // TODO: Update copyright text.

    4 // </copyright>

    5 // ———————————————————————–

    6

    7 namespace FactoryMethod

    8 {

    9     using System;

   10     using System.Collections.Generic;

   11     using System.Linq;

   12     using System.Text;

   13

   14     /// <summary>

   15     /// TODO: Update summary.

   16     /// </summary>

   17     public class ProductAFactory:ProductFactory

   18     {

   19         public override Product CreateProductInstance()

   20         {

   21             return new ProductA();

   22         }

   23     }

   24 }

   25

 ProductBFactory.cs

    1 // ———————————————————————–

    2 // <copyright file=”ProductBFactory.cs” company=””>

    3 // TODO: Update copyright text.

    4 // </copyright>

    5 // ———————————————————————–

    6

    7 namespace FactoryMethod

    8 {

    9     using System;

   10     using System.Collections.Generic;

   11     using System.Linq;

   12     using System.Text;

   13

   14     /// <summary>

   15     /// TODO: Update summary.

   16     /// </summary>

   17     public class ProductBFactory:ProductFactory

   18     {

   19         public override Product CreateProductInstance()

   20         {

   21             return new ProductB();

   22

   23         }

   24     }

   25 }

   26

 

Product.cs

    1 // ———————————————————————–

    2 // <copyright file=”Product.cs” company=””>

    3 // TODO: Update copyright text.

    4 // </copyright>

    5 // ———————————————————————–

    6

    7 namespace FactoryMethod

    8 {

    9     using System;

   10     using System.Collections.Generic;

   11     using System.Linq;

   12     using System.Text;

   13

   14     /// <summary>

   15     /// TODO: Update summary.

   16     /// </summary>

   17     public abstract class Product

   18     {

   19         public abstract string Name { get; set; }

   20     }

   21 }

   22

 ProductB.cs

    1 // ———————————————————————–

    2 // <copyright file=”ProductB.cs” company=””>

    3 // TODO: Update copyright text.

    4 // </copyright>

    5 // ———————————————————————–

    6

    7 namespace FactoryMethod

    8 {

    9     using System;

   10     using System.Collections.Generic;

   11     using System.Linq;

   12     using System.Text;

   13

   14     /// <summary>

   15     /// TODO: Update summary.

   16     /// </summary>

   17     public class ProductB:Product

   18     {

   19          public ProductB()

   20         {

   21

   22             Name = ProductB;

   23         }

   24         public override string Name { get; set; }

   25     }

   26 }

   27

 ProductA.cs

    1 // ———————————————————————–

    2 // <copyright file=”ProductA.cs” company=””>

    3 // TODO: Update copyright text.

    4 // </copyright>

    5 // ———————————————————————–

    6

    7 namespace FactoryMethod

    8 {

    9     using System;

   10     using System.Collections.Generic;

   11     using System.Linq;

   12     using System.Text;

   13

   14     /// <summary>

   15     /// TODO: Update summary.

   16     /// </summary>

   17     public class ProductA:Product

   18     {

   19         public ProductA()

   20         {

   21

   22             Name = ProductA;

   23         }

   24

   25         public override string Name { get; set; }

   26     }

   27 }

   28

 

Sample implementation of Abstract Factory pattern

 

Here’s the class diagram:

 

class diagram

As said earlier, the Abstract factory pattern adds another layer of abstraction to the factory method pattern so the Product/ProductFactory classes that we used earlier in Factory Method pattern implementation would be reused here.

 

    1 // ———————————————————————–

    2 // <copyright file=”ApplicationCaller.cs” company=””>

    3 // TODO: Update copyright text.

    4 // </copyright>

    5 // ———————————————————————–

    6

    7 namespace FactoryMethod

    8 {

    9     using System;

   10     using System.Collections.Generic;

   11     using System.Linq;

   12     using System.Text;

   13

   14     /// <summary>

   15     /// TODO: Update summary.

   16     /// </summary>

   17     public class ApplicationCaller

   18     {

   19         public Product PassFactory(ProductFactory pf)

   20         {

   21

   22           Product product = pf.CreateProductInstance();

   23             return product;

   24         }

   25     }

   26 }

   27

    1 using System;

    2 using System.Collections.Generic;

    3 using System.Linq;

    4 using System.Text;

    5

    6 namespace FactoryMethod

    7 {

    8     using System.Configuration;

    9     using System.Net.Mime;

   10

   11     class Program

   12     {

   13         static ProductFactory CreateSpecificFactory()

   14         {

   15

   16             string facType = System.Configuration.ConfigurationSettings.AppSettings[FactoryName];

   17             if (facType == “ProductAFactory”)

   18             {

   19                 return new ProductAFactory();

   20             }

   21             else

   22             {

   23                 return new ProductBFactory();

   24             }

   25         }

   26

   27         static void Main(string[] args)

   28         {

   29             Console.WriteLine(new ApplicationCaller().PassFactory(CreateSpecificFactory()).Name);

   30

   31         }

   32     }

   33 }

   34

Add finally the type of factory to be used would be decided at runtime, in this sample we are reading this information from a config file and passing it to the application.

    1 <?xml version=1.0encoding=utf-8 ?>

    2 <configuration>

    3   <appSettings>

    4     <add key=FactoryNamevalue=ProductBFactory/>

    5   </appSettings>

    6 </configuration>

Standard
Design Patterns

Template Method Design Pattern implementation in C#

First of all I’ll just put this pattern in context and describe its intent as in the GOF book:

Template Method:

Define the skeleton of an algorithm in an operation, deferring some steps to

Subclasses. Template Method lets subclasses redefine certain steps of an algorithm

without changing the Algorithm’s Structure. 

 

Usage:

When you are certain about the High Level steps involved in an Algorithm/Work flow you can use the Template Pattern which allows the Base Class to define the Sequence of the Steps but permits the Sub classes to alter the implementation of any/all steps.

Example in the .Net framework:

The most common example is the Asp.Net Page Life Cycle. The Page Life Cycle has a few methods which are called in a sequence but we have the liberty to modify the functionality of any of the methods by overriding them.

Sample implementation of Template Method Pattern:

Let’s see the class diagram first:

Template Design Pattern

Template Design Pattern

And here goes the code:

 

namespace TemplateMethodPatternConsoleApplication.Algorithm
{
    public abstract class AlgorithmBase
    {
        public void DoSomething()
        {
            DoSomethingStep1();
            DoSomethingStep2();
            DoSomethingStep3();
        }
 
        protected abstract void DoSomethingStep3();
 
 
        protected abstract void DoSomethingStep2();
 
 
        protected abstract void DoSomethingStep1();
   
    }
}
namespace TemplateMethodPatternConsoleApplication.Algorithm
{
    class AlgorithmChild1:AlgorithmBase
    {
        protected override void DoSomethingStep3()
        {
            Console.WriteLine("Algorithm 1 Step 3");
        }
 
        protected override void DoSomethingStep2()
        {
            Console.WriteLine("Algorithm 1 Step 2");
        }
 
        protected override void DoSomethingStep1()
        {
            Console.WriteLine("Algorithm 1 Step 1");
        }
    }
}
namespace TemplateMethodPatternConsoleApplication.Algorithm
{
    class AlgorithmChild2:AlgorithmBase
    {
 
        protected override void DoSomethingStep3()
        {
            Console.WriteLine("Algorithm 2 Step 3");
        }
 
        protected override void DoSomethingStep2()
        {
            Console.WriteLine("Algorithm 2 Step 2");
        }
 
        protected override void DoSomethingStep1()
        {
            Console.WriteLine("Algorithm 2 Step 1");
        }
    }
}


Calling Code:

namespace TemplateMethodPatternConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            var algo = new AlgorithmChild2();
            algo.DoSomething();
            Console.ReadLine();
        }
 
    }
}
Standard
Algorithms

How to find if Two Strings are Anagrams of each other in C#?

In case you are not aware, Anagrams are strings whose characters are some permutation of each other.

For ex: cat, act, tac are anagrams as they are all permutations of ‘a’, ‘c’ and ‘t’.

Here’s the code which checks for the same:

public static bool AreTwoStringsAnagrams(string s1, string s2)
        {
            // Short circuit if any of the strings are null or whitespace 
            if (string.IsNullOrWhiteSpace(s1) || string.IsNullOrWhiteSpace(s2))
            {
                return false;
            }
 
            // Short circuit if the strings are not of equal length
            if (s1.Length != s2.Length)
            {
                return false;
            }
 
            // O(L) space complexity, where L is the Lenght of the largest string
            char[] c1 = s1.ToLower().ToCharArray();
            char[] c2 = s2.ToLower().ToCharArray();
 
            Array.Sort(c1);
            Array.Sort(c2);
 
            for(int i = 0; i < c1.Length; i++)
            {
                if (c1[i] != c2[i])
                {
                    return false;
                }
            }
 
            return true;
        }
Standard
Design Patterns

Singleton Design Pattern C#

The most simplest of the Design Patterns to implement, but one has to keep in check few corner cases to get it right.

Let us see a naive implementation first which works well (at least it will look like it works well :-P) but then it wouldn’t work in a multi threaded environment.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace SingletonPatternConsoleApplication
{
 
    /// <summary>
    /// Singleton class, only one instance would be created at any time.
    /// </summary>
    class Singleton
    {
        private static Singleton singleton;
 
        /// <summary>
        /// Private constructor in order to force singleton
        /// </summary>
        private Singleton()
        {
 
        }
 
        public static Singleton CreateSingleton()
        {
            //This code isn't Thread Safe and can lead to multiple instances of Singleton Class.
            //If two threads come to this point at the same instance when singleton is still null, then there's a chance that both would create a new instance.
            if (singleton == null)
            {
                singleton = new Singleton();
                Console.WriteLine("Instance created!");
            }
            return singleton;
 
        }
 
 
    }
}

Let us see the thread safe version of the Singleton Pattern:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace SingletonPatternConsoleApplication
{
 
    /// <summary>
    /// Singleton class, only one instance would be created at any time.
    /// </summary>
    class SingletonThreadSafe
    {
        private static readonly object _lock = new object();
        private static SingletonThreadSafe singleton;
 
        /// <summary>
        /// Private constructor in order to force singleton
        /// </summary>
        private SingletonThreadSafe(){ }
 
        public static SingletonThreadSafe CreateSingleton()
        {
            lock (_lock)
            {
 
                if (singleton == null)
                {
                    singleton = new SingletonThreadSafe();
                }
                return singleton;
            }
        }
    }
}
Standard
Uncategorized

Getting the GetHashCode and Equals Method Implementation right in C#

Why & When…

Let us first understand why and when would you want to override the default behavior of these two methods in the Object class.

Here is the ‘When’:

  •  If your item will be used as a key in a Dictionary or a HashSet<T>. Also when you want to compare elements of a Type by calling equals rather than manually checking for equality.

Here is the ‘Why’:

  •  The Equals() and the GetHashCode() methods are used to derive if two Keys are equal or not in a HashSet and on that basis they land up at a place in the Table or not considered at all in case they are duplicates.

Points to consider while implementing these methods

  • If Equals() return True for two Objects then the Objects must return the same value for GetHashCode()
  • If GetHashCode() returns the same value for two Objects, it is not necessary that the Objects would be equal. Equals method would be called to see if it is a real equality or not.

Let us see an Example

First of all you need to identify the fields of a Type which would uniquely identify your Object. In this example I have considered one of the Data Structures I am currently working with in my Project and here is how it looks like:

/// <summary>
/// This method implements Equality using Value semantics since this class is immutable
/// and "Equals" in our case means that all corresponding Property values are the same
/// </summary>
/// <param name="obj">An instance of a <see cref="Coverage"/></param>
/// <returns>True of the "values" of All properties are the same</returns>
public override bool Equals(object obj)
{
    var criteria = obj as AvailableCoverage;
 
    if (criteria == null)
    {
        return false;
    }
 
    return this.VehicleCoverageCode == criteria.VehicleCoverageCode
           && this.VehicleCoverageLimitCode == criteria.VehicleCoverageLimitCode
           && this.VehicleCoverageDeductibleCode == criteria.VehicleCoverageDeductibleCode
           && this.VehicleId == criteria.VehicleId;
}
 
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// The integer
/// </returns>
public override int GetHashCode()
{
    var hashCode = 37;
 
    if (this.VehicleCoverageCode != null)
    {
        hashCode = (hashCode * 23+ this.VehicleCoverageCode.GetHashCode();
    }
 
    if (this.VehicleCoverageLimitCode != null)
    {
        hashCode = (hashCode * 23+ this.VehicleCoverageLimitCode.GetHashCode();
    }
 
    if (this.VehicleCoverageDeductibleCode != null)
    {
        hashCode = (hashCode * 23+ this.VehicleCoverageDeductibleCode.GetHashCode();
    }
 
    hashCode = (hashCode * 23+ this.VehicleId.GetHashCode();
 
    return hashCode;
}
Standard