我的类的C#泛型列表包含找不到我的实例的方法

时间:2010-11-22 17:36:50

标签: c# equals contains generic-list iequatable

我们的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,因为即使p1p4是不同的实例,Equals方法中的继承BasePage方法也会比较GUID要求的IBaseTemplate属性。我在这里错过了什么吗?

我查看了docs for List(T)'s Contains method,我正在实施IEquatable<T>.Equals,其中TIBaseTemplate

5 个答案:

答案 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>

  

This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable<T>.Equals method for T (the type of values in the list).

由于您的类未实现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