1

我们为我们的 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);
  }

}

登陆页面.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) {}
}

示例页面.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,因为即使p1throughp4是不同的实例,继承的Equals方法 fromBasePage也会GUID根据IBaseTemplate. 我在这里错过了什么吗?

我查看了List(T) 的 Contains 方法的文档,我正在实现IEquatable<T>.Equalswhere Tis IBaseTemplate

4

5 回答 5

2

您还需要覆盖Object.Equals(Object)- 请参阅此链接

于 2010-11-22T17:43:59.927 回答
1

我敢打赌,在幕后,这行代码:

p1.Equals(p4)

 is actually calling upon the Equals method of `Object` rather than yours.

尝试让您的接口实现IEqualityComparerEquals并在接口定义中保留显式。

于 2010-11-22T17:42:36.653 回答
0

如果您希望方法使用该方法,则您的BasePage类型需要实现IEquatable<BasePage>,而不是。IEquatable<IBaseTemplate>ContainsIEquatable.Equals

此方法通过使用默认相等比较器来确定相等性,该比较器由对象的 IEquatable<T>.Equals方法实现定义T (列表中值的类型)。

因为您的类没有实现IEquatable<BasePage>,所以该Contains方法回退到使用虚拟的非泛型object.Equals方法。在您的课程中覆盖该方法BasePage应该可以解决问题

于 2010-11-22T17:47:41.323 回答
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
}
于 2010-11-22T18:00:11.807 回答
-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
于 2010-11-22T17:40:09.983 回答