Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 181117
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 11, 20262026-05-11T14:42:19+00:00 2026-05-11T14:42:19+00:00

Background I have two objects which have bidirectional association between them in a C#

  • 0

Background

I have two objects which have bidirectional association between them in a C# project I am working on. I need to be able to check for value equality (vs reference equality) for a number of reasons (e.g to use them in collections) and so I am implementing IEquatable and the related functions.

Assumptions

  • I am using C# 3.0, .NET 3.5, and Visual Studio 2008 (although it shouldn’t matter for equality comparison routine issue).

Constraints

Any solution must:

  • Allow for bidirectional association to remain intact while permitting checking for value equality.
  • Allow the external uses of the class to call Equals(Object obj) or Equals(T class) from IEquatable and receive the proper behavior (such as in System.Collections.Generic).

Problem

When implementing IEquatable to provide checking for value equality on types with bidirectional association, infinite recursion occurs resulting in a stack overflow.

NOTE: Similarly, using all of the fields of a class in the GetHashCode calculation will result in a similar infinite recursion and resulting stack overflow issue.


Question

How do you check for value equality between two objects which have bidirectional association without resulting in a stack overflow?


Code

NOTE: This code is notional to display the issue, not demonstrate the actual class design I’m using which is running into this problem

using System;  namespace EqualityWithBiDirectionalAssociation {      public class Person : IEquatable<Person>     {         private string _firstName;         private string _lastName;         private Address _address;          public Person(string firstName, string lastName, Address address)         {             FirstName = firstName;             LastName = lastName;             Address = address;         }          public virtual Address Address         {             get { return _address; }             set { _address = value; }         }          public virtual string FirstName         {             get { return _firstName; }             set { _firstName = value; }         }          public virtual string LastName         {             get { return _lastName; }             set { _lastName = value; }         }          public override bool Equals(object obj)         {             // Use 'as' rather than a cast to get a null rather an exception             // if the object isn't convertible             Person person = obj as Person;             return this.Equals(person);         }          public override int GetHashCode()         {             string composite = FirstName + LastName;             return composite.GetHashCode();         }           #region IEquatable<Person> Members          public virtual bool Equals(Person other)         {             // Per MSDN documentation, x.Equals(null) should return false             if ((object)other == null)             {                 return false;             }              return (this.Address.Equals(other.Address)                 && this.FirstName.Equals(other.FirstName)                 && this.LastName.Equals(other.LastName));         }          #endregion      }      public class Address : IEquatable<Address>     {         private string _streetName;         private string _city;         private string _state;         private Person _resident;          public Address(string city, string state, string streetName)         {             City = city;             State = state;             StreetName = streetName;             _resident = null;         }          public virtual string City         {             get { return _city; }             set { _city = value; }         }          public virtual Person Resident         {             get { return _resident; }             set { _resident = value; }         }          public virtual string State         {             get { return _state; }             set { _state = value; }         }          public virtual string StreetName         {             get { return _streetName; }             set { _streetName = value; }         }          public override bool Equals(object obj)         {             // Use 'as' rather than a cast to get a null rather an exception             // if the object isn't convertible             Address address = obj as Address;             return this.Equals(address);         }          public override int GetHashCode()         {             string composite = StreetName + City + State;             return composite.GetHashCode();         }           #region IEquatable<Address> Members          public virtual bool Equals(Address other)         {             // Per MSDN documentation, x.Equals(null) should return false             if ((object)other == null)             {                 return false;             }              return (this.City.Equals(other.City)                 && this.State.Equals(other.State)                 && this.StreetName.Equals(other.StreetName)                 && this.Resident.Equals(other.Resident));         }          #endregion     }      public class Program     {         static void Main(string[] args)         {             Address address1 = new Address('seattle', 'washington', 'Awesome St');             Address address2 = new Address('seattle', 'washington', 'Awesome St');              Person person1 = new Person('John', 'Doe', address1);              address1.Resident = person1;             address2.Resident = person1;              if (address1.Equals(address2)) // <-- Generates a stack overflow!             {                 Console.WriteLine('The two addresses are equal');             }              Person person2 = new Person('John', 'Doe', address2);             address2.Resident = person2;              if (address1.Equals(address2)) // <-- Generates a stack overflow!             {                 Console.WriteLine('The two addresses are equal');             }              Console.Read();         }     } } 
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. 2026-05-11T14:42:19+00:00Added an answer on May 11, 2026 at 2:42 pm

    If redesigning the class structure to remove the bidirectional association is possible and reduces the number of problems associated with the implementation, then this is preferred solution.

    If this redesign is not possible or introduces equal or greater implementation issues, then one possible solution is to use a specialized Equals method to be called by Equals methods of the classes involved in the bidirectional association. As Mehrdad stated, this shouldn’t be too big of a deal since the requirements explicitly ask for this coupling, so you are not introducing one by doing this.


    Code

    Here is an implementation of this that keeps the specialized methods checking only their own fields. This reduces maintenance problems vs having each class do a per-property comparison of the other class.

    using System;  namespace EqualityWithBiDirectionalAssociation {      public class Person : IEquatable<Person>     {         private string _firstName;         private string _lastName;         private Address _address;          public Person(string firstName, string lastName, Address address)         {             FirstName = firstName;             LastName = lastName;             Address = address;         }          public virtual Address Address         {             get { return _address; }             set { _address = value; }         }          public virtual string FirstName         {             get { return _firstName; }             set { _firstName = value; }         }          public virtual string LastName         {             get { return _lastName; }             set { _lastName = value; }         }          public override bool Equals(object obj)         {             // Use 'as' rather than a cast to get a null rather an exception             // if the object isn't convertible             Person person = obj as Person;             return this.Equals(person);         }          public override int GetHashCode()         {             string composite = FirstName + LastName;             return composite.GetHashCode();         }          internal virtual bool EqualsIgnoringAddress(Person other)         {             // Per MSDN documentation, x.Equals(null) should return false             if ((object)other == null)             {                 return false;             }              return ( this.FirstName.Equals(other.FirstName)                 && this.LastName.Equals(other.LastName));         }          #region IEquatable<Person> Members          public virtual bool Equals(Person other)         {             // Per MSDN documentation, x.Equals(null) should return false             if ((object)other == null)             {                 return false;             }              return (this.Address.EqualsIgnoringPerson(other.Address)   // Don't have Address check it's person                 && this.FirstName.Equals(other.FirstName)                 && this.LastName.Equals(other.LastName));         }          #endregion      }      public class Address : IEquatable<Address>     {         private string _streetName;         private string _city;         private string _state;         private Person _resident;          public Address(string city, string state, string streetName)         {             City = city;             State = state;             StreetName = streetName;             _resident = null;         }          public virtual string City         {             get { return _city; }             set { _city = value; }         }          public virtual Person Resident         {             get { return _resident; }             set { _resident = value; }         }          public virtual string State         {             get { return _state; }             set { _state = value; }         }          public virtual string StreetName         {             get { return _streetName; }             set { _streetName = value; }         }          public override bool Equals(object obj)         {             // Use 'as' rather than a cast to get a null rather an exception             // if the object isn't convertible             Address address = obj as Address;             return this.Equals(address);         }          public override int GetHashCode()         {             string composite = StreetName + City + State;             return composite.GetHashCode();         }            internal virtual bool EqualsIgnoringPerson(Address other)         {             // Per MSDN documentation, x.Equals(null) should return false             if ((object)other == null)             {                 return false;             }              return (this.City.Equals(other.City)                 && this.State.Equals(other.State)                 && this.StreetName.Equals(other.StreetName));         }          #region IEquatable<Address> Members          public virtual bool Equals(Address other)         {             // Per MSDN documentation, x.Equals(null) should return false             if ((object)other == null)             {                 return false;             }              return (this.City.Equals(other.City)                 && this.State.Equals(other.State)                 && this.StreetName.Equals(other.StreetName)                 && this.Resident.EqualsIgnoringAddress(other.Resident));         }          #endregion     }      public class Program     {         static void Main(string[] args)         {             Address address1 = new Address('seattle', 'washington', 'Awesome St');             Address address2 = new Address('seattle', 'washington', 'Awesome St');              Person person1 = new Person('John', 'Doe', address1);              address1.Resident = person1;             address2.Resident = person1;              if (address1.Equals(address2)) // <-- No stack overflow!             {                 Console.WriteLine('The two addresses are equal');             }              Person person2 = new Person('John', 'Doe', address2);             address2.Resident = person2;              if (address1.Equals(address2)) // <-- No a stack overflow!             {                 Console.WriteLine('The two addresses are equal');             }              Console.Read();         }     } } 

    Output

    The two addresses are equal.

    The two addresses are equal.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Ask A Question

Stats

  • Questions 166k
  • Answers 166k
  • Best Answers 0
  • User 1
  • Popular
  • Answers
  • Editorial Team

    How to approach applying for a job at a company ...

    • 7 Answers
  • Editorial Team

    What is a programmer’s life like?

    • 5 Answers
  • Editorial Team

    How to handle personal stress caused by utterly incompetent and ...

    • 5 Answers
  • Editorial Team
    Editorial Team added an answer this is how i do it: in my .hbm.xml <?xml… May 12, 2026 at 1:23 pm
  • Editorial Team
    Editorial Team added an answer You could but, later on in the project, you shouldn't… May 12, 2026 at 1:23 pm
  • Editorial Team
    Editorial Team added an answer MySQL has been ported to OpenVMS/Alpha. If you could migrate… May 12, 2026 at 1:23 pm

Related Questions

I ran into a scenario where LINQ to SQL acts very strangely. I would
I have a list of objects which need to be output 2 items per
First off, there's a bit of background to this issue available on my blog:
When trying to use delegates in C# to solve a problem in a functional

Trending Tags

analytics british company computer developers django employee employer english facebook french google interview javascript language life php programmer programs salary

Top Members

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.