C# C语言中具有双向关联的值相等# 背景
在我正在进行的一个C#项目中,我有两个对象,它们之间有双向关联。由于许多原因(例如在集合中使用它们),我需要能够检查值相等(与引用相等),因此我正在实现IEquatable和相关函数 假设C# C语言中具有双向关联的值相等# 背景,c#,.net,equals,iequatable,C#,.net,Equals,Iequatable,在我正在进行的一个C#项目中,我有两个对象,它们之间有双向关联。由于许多原因(例如在集合中使用它们),我需要能够检查值相等(与引用相等),因此我正在实现IEquatable和相关函数 假设 我使用的是C#3.0、.NET3.5和VisualStudio2008(尽管这对于平等性比较例程问题来说并不重要) 约束条件 任何解决方案都必须: 允许双向关联保持不变,同时允许检查值是否相等 允许该类的外部使用从IEquatable调用Equals(Object obj)或Equals(T class
- 我使用的是C#3.0、.NET3.5和VisualStudio2008(尽管这对于平等性比较例程问题来说并不重要)
- 允许双向关联保持不变,同时允许检查值是否相等
- 允许该类的外部使用从IEquatable调用Equals(Object obj)或Equals(T class),并接收正确的行为(例如在System.Collections.Generic中)
问题: 如何在不导致堆栈溢出的情况下检查具有双向关联的两个对象之间的值相等性?
代码 注意:此代码只是用于显示问题的概念性代码,而不是演示遇到此问题的实际类设计
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();
}
}
}
使用系统;
命名空间EqualityWithBidirectionAlasAssociation
{
公共类人士:IEquatable
{
私有字符串_firstName;
私有字符串_lastName;
私人地址(u地址);;
公众人物(字符串名、字符串名、地址)
{
名字=名字;
LastName=LastName;
地址=地址;
}
公共虚拟地址
{
获取{return\u address;}
设置{u address=value;}
}
公共虚拟字符串名
{
获取{return\u firstName;}
设置{u firstName=value;}
}
公共虚拟字符串LastName
{
获取{return\u lastName;}
设置{u lastName=value;}
}
公共覆盖布尔等于(对象对象对象)
{
//使用“as”而不是强制转换来获取null而不是异常
//如果对象不可转换
Person-Person=obj-as-Person;
返回此。等于(人);
}
公共覆盖int GetHashCode()
{
字符串组合=FirstName+LastName;
返回composite.GetHashCode();
}
#区域可容纳成员
公共虚拟bool等于(其他人)
{
//根据MSDN文档,x.Equals(null)应返回false
if((对象)other==null)
{
返回false;
}
return(this.Address.Equals)(other.Address)
&&this.FirstName.Equals(other.FirstName)
&&this.LastName.Equals(other.LastName));
}
#端区
}
公共类地址:IEquatable
{
私有字符串(街道名称);
私人字符串(城市),;
私有字符串状态;
私人居民;
公共地址(字符串城市、字符串州、字符串街道名称)
{
城市=城市;
状态=状态;
街道名称=街道名称;
_常驻=空;
}
公共虚拟城市
{
获取{return\u city;}
设置{u city=value;}
}
公共虚拟人居民
{
获取{return\u resident;}
设置{u常驻=值;}
}
公共虚拟字符串状态
{
获取{return\u state;}
设置{u state=value;}
}
公共虚拟字符串StreetName
{
获取{return\u streetName;}
设置{u streetName=value;}
}
公共覆盖布尔等于(对象对象对象)
{
//使用“as”而不是强制转换来获取null而不是异常
//如果对象不可转换
地址=obj作为地址;
返回此.Equals(地址);
}
公共覆盖int GetHashCode()
{
字符串组合=街道名称+城市+州;
返回composite.GetHashCode();
}
#区域可容纳成员
公共虚拟布尔等于(地址其他)
{
//根据MSDN文档,x.Equals(null)应返回false
if((对象)other==null)
{
返回false;
}
return(this.City.Equals(other.City)
&&this.State.Equals(其他.State)
&&this.StreetName.Equals(其他.StreetName)
&&这个.Resident.Equals(其他.Resident));
}
#端区
}
公共课程
{
静态void Main(字符串[]参数)
{
地址1=新地址(“西雅图”、“华盛顿”、“威盛街”);
地址2=新地址(“西雅图”、“华盛顿”、“威盛街”);
Person person1=新人(“约翰”,“多伊”,地址1);
地址1.居民=个人1;
地址2.居民=个人1;
如果(AddiSr1.1(AddiSr2))//p>你将类连接得太紧,混合了值和引用。你应该考虑检查一个类的引用相等,或者让它们知道彼此。(通过为特定类提供internal
专用的Equals
方法或手动检查其他类的值相等性)。这应该不是什么大问题,因为您的需求明确
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); // wrong
this.FirstName.Equals(person.FirstName)
this.LastName.Equals(person.LastName)
// and so on
}
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();
}
}
}