我们的CMS有几个类,我正在努力使等式工作,所以我可以检查一般列表是否包含一个项目。我们有一些继承层,我将在下面给你看。在此之下,我将向您展示一些与我的期望相反的示例代码。如果你看到我做错了什么,请告诉我。我已经减少了以下示例,只是为了向您展示相关的部分。我的实际课程要大得多,但我认为这是你需要看到的一切。
IBaseTemplate.cs
public interface IBaseTemplate {
bool Equals(IBaseTemplate other);
string GUID { get; }
}
BasePage.cs
public class BasePage : System.Web.UI.Page, IBaseTemplate, IEquatable<IBaseTemplate> {
// code to define properties, including GUID
// various constructors
public BasePage(string GUID) {
this.GUID = GUID;
}
// interface methods
public bool Equals(IBaseTemplate other) {
return (this.GUID == other.GUID);
}
}
LandingPage.cs
public class LandingPage : BasePage {
// a bunch of extra properties and method specific to LandingPage
// but NO definition for Equals since that's taken care of in BasePage
public LandingPage(string GUID) : base(GUID) {}
}
SamplePage.aspx.cs
var p1 = new LandingPage("{3473AEF9-7382-43E2-B783-DB9B88B825C5}");
var p2 = new LandingPage("{3473AEF9-7382-43E2-B783-DB9B88B825C5}");
var p3 = new LandingPage("{3473AEF9-7382-43E2-B783-DB9B88B825C5}");
var p4 = new LandingPage("{3473AEF9-7382-43E2-B783-DB9B88B825C5}");
var coll = new List<LandingPage>();
coll.Add(p1);
coll.Add(p2);
coll.Add(p3);
p1.Equals(p4); // True, as expected
coll.Contains(p4); // False, but I expect True here!
我希望coll.Contains(p4)
返回true
,因为即使p1
到p4
是不同的实例,Equals
方法中的继承BasePage
方法也会比较GUID
要求的IBaseTemplate
属性。我在这里错过了什么吗?
我查看了docs for List(T)'s Contains method,我正在实施IEquatable<T>.Equals
,其中T
为IBaseTemplate
。
答案 0 :(得分:2)
您还需要覆盖Object.Equals(Object)
- 请参阅this link。
答案 1 :(得分:1)
我敢打赌,这一行代码:
p1.Equals(p4)
is actually calling upon the Equals method of `Object` rather than yours.
尝试使用您的界面实现IEqualityComparer并将明确的Equals
保留在您的界面定义之外。
答案 2 :(得分:0)
如果您希望Contains
方法使用IEquatable.Equals
方法,则BasePage
类型需要实现IEquatable<BasePage>
,而不是IEquatable<IBaseTemplate>
。
由于您的类未实现IEquatable<BasePage>
,因此Contains
方法会回退到使用虚拟的非通用object.Equals
方法。在BasePage
类中覆盖该方法应该可以解决这个问题
答案 3 :(得分:0)
对List.Contains(T item) 的调用通过使用默认的相等比较器来确定相等性,由对象的T的IEquatable.Equals方法的实现(列表中的值的类型)定义。
因此,要在列表中找到的项目需要提供IEquatable的实现,并且还需要覆盖Equals(Object obj)方法以调用等值逻辑。
例如,假设您有这样的界面:
public interface IRole : INotifyPropertyChanged, INotifyPropertyChanging
{
#region ConcurrencyToken
/// <summary>
/// Gets the unique binary concurrency token for the security role.
/// </summary>
/// <value>
/// A <see cref="IList{Byte}"/> collection that contains the unique binary concurrency token for the security role.
/// </value>
IList<byte> ConcurrencyToken
{
get;
}
#endregion
#region Description
/// <summary>
/// Gets or sets the description of the security role.
/// </summary>
/// <value>
/// The human readable description of the security role.
/// </value>
string Description
{
get;
set;
}
#endregion
#region Id
/// <summary>
/// Gets or sets the unique identifier for the security role.
/// </summary>
/// <value>
/// A <see cref="Int32"/> value that represents the unique identifier for the security role.
/// </value>
int Id
{
get;
set;
}
#endregion
#region LastModifiedBy
/// <summary>
/// Gets or sets the name of the user or process that last modified the security role.
/// </summary>
/// <value>
/// The name of the user or process that last modified the security role.
/// </value>
string LastModifiedBy
{
get;
set;
}
#endregion
#region LastModifiedOn
/// <summary>
/// Gets or sets the date and time at which the security role was last modified.
/// </summary>
/// <value>
/// A <see cref="Nullable{DateTime}"/> that represents the date and time at which the security role was last modified.
/// </value>
DateTime? LastModifiedOn
{
get;
set;
}
#endregion
#region Name
/// <summary>
/// Gets or sets the name of the security role.
/// </summary>
/// <value>
/// The human readable name of the security role.
/// </value>
string Name
{
get;
set;
}
#endregion
}
此接口的默认实现可能是这个,并注意IEquatable的实现方式:
public class Role : EntityBase, IRole, IEquatable<Role>
{
//=======================================================================================================
// Constructors
//=======================================================================================================
#region Role()
/// <summary>
/// Initializes a new instance of <see cref="Role"/> class.
/// </summary>
public Role()
{
}
#endregion
#region Role(IEnumerable<byte> concurrencyToken)
/// <summary>
/// Initializes a new instance of <see cref="Role"/> class
/// using the specified unique binary concurrency token.
/// </summary>
/// <param name="concurrencyToken">The unique binary concurrency token for the security role.</param>
/// <exception cref="ArgumentNullException">The <paramref name="concurrencyToken"/> is a <see langword="null"/> reference (Nothing in Visual Basic).</exception>
public Role(IEnumerable<byte> concurrencyToken) : base(concurrencyToken)
{
}
#endregion
//=======================================================================================================
// Public Methods
//=======================================================================================================
#region ToString()
/// <summary>
/// Returns a <see cref="String"/> that represents the current <see cref="Role"/>.
/// </summary>
/// <returns>
/// A <see cref="String"/> that represents the current <see cref="Role"/>.
/// </returns>
public override string ToString()
{
return this.Name;
}
#endregion
//=======================================================================================================
// IEquatable<Role> Implementation
//=======================================================================================================
#region Equals(Role other)
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns><see langword="true"/> if the current object is equal to the other parameter; otherwise, <see langword="false"/>.</returns>
public bool Equals(Role other)
{
if (other == null)
{
return false;
}
if (!String.Equals(this.Description, other.Description, StringComparison.Ordinal))
{
return false;
}
else if (!Int32.Equals(this.Id, other.Id))
{
return false;
}
else if (!String.Equals(this.Name, other.Name, StringComparison.Ordinal))
{
return false;
}
return true;
}
#endregion
#region Equals(object obj)
/// <summary>
/// Determines whether the specified <see cref="Object"/> is equal to the current <see cref="Object"/>.
/// </summary>
/// <param name="obj">The <see cref="Object"/> to compare with the current <see cref="Object"/>.</param>
/// <returns>
/// <see langword="true"/> if the specified <see cref="Object"/> is equal to the current <see cref="Object"/>; otherwise, <see langword="false"/>.
/// </returns>
public override bool Equals(object obj)
{
return this.Equals(obj as Role);
}
#endregion
#region GetHashCode()
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>A 32-bit signed integer hash code.</returns>
/// <a href="http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx"/>
public override int GetHashCode()
{
int descriptionHashCode = this.Description.GetHashCode();
int idHashCode = this.Id.GetHashCode();
int nameHashCode = this.Name.GetHashCode();
/*
* The 23 and 37 are arbitrary numbers which are co-prime.
*
* The benefit of the below over the XOR (^) method is that if you have a type
* which has two values which are frequently the same, XORing those values
* will always give the same result (0) whereas the above will
* differentiate between them unless you're very unlucky.
*/
int hashCode = 23;
hashCode = hashCode * 37 + descriptionHashCode;
hashCode = hashCode * 37 + idHashCode;
hashCode = hashCode * 37 + nameHashCode;
return hashCode;
}
#endregion
//=======================================================================================================
// IRole Implementation
//=======================================================================================================
#region Description
/// <summary>
/// Gets or sets the description of this security role.
/// </summary>
/// <value>
/// The human readable description of this security role. The default value is an <see cref="String.Empty"/> string.
/// </value>
[DataMember()]
public string Description
{
get
{
return _roleDescription;
}
set
{
if (PropertyChangeNotifier.AreNotEqual(_roleDescription, value))
{
using (new PropertyChangeNotifier(OnPropertyChanging, OnPropertyChanged))
{
_roleDescription = !String.IsNullOrEmpty(value) ? value : String.Empty;
}
}
}
}
private string _roleDescription = String.Empty;
#endregion
#region Id
/// <summary>
/// Gets or sets the unique identifier for this security role.
/// </summary>
/// <value>
/// A <see cref="Int32"/> value that represents the unique identifier for this security role.
/// The default value is <i>zero</i>.
/// </value>
[DataMember()]
public int Id
{
get
{
return _roleId;
}
set
{
if (PropertyChangeNotifier.AreNotEqual(_roleId, value))
{
using (new PropertyChangeNotifier(OnPropertyChanging, OnPropertyChanged))
{
_roleId = value;
}
}
}
}
private int _roleId;
#endregion
#region Name
/// <summary>
/// Gets or sets the name of this security role.
/// </summary>
/// <value>
/// The human readable name of this security role. The default value is an <see cref="String.Empty"/> string.
/// </value>
[DataMember()]
public string Name
{
get
{
return _roleName;
}
set
{
if (PropertyChangeNotifier.AreNotEqual(_roleName, value))
{
using (new PropertyChangeNotifier(OnPropertyChanging, OnPropertyChanged))
{
_roleName = !String.IsNullOrEmpty(value) ? value : String.Empty;
}
}
}
}
private string _roleName = String.Empty;
#endregion
}
答案 4 :(得分:-1)
您需要将p4实例实际添加到集合中。
coll.Add(p4);
修改强>
为什么不直接比较GUID?
coll.Add(p1.GUID);
coll.Add(p2.GUID);
coll.Add(p3.GUID);
coll.Contains(p4.GUID); //expect this to be true