简体   繁体   English

为什么当我调用此哈希集的删除操作时,它既不使用 gethashcode 也不使用相等实现?

[英]Why when i call the remove operation of this hashset it doesn't use the gethashcode neither the equality implementation?

i am doing an app to manage the creation of role-playing sessions, but i am having problems with the section to do rules summaries so the Master doesnt have to be reading the core book every sec, i have the data structures in this way.我正在做一个应用程序来管理角色扮演会话的创建,但是我在做规则摘要的部分有问题,所以大师不必每秒钟都阅读核心书,我有这种数据结构。

User have a list of campaigns, that campaign have a list of scenaries and that scenaries have a list of adventures.用户有一个活动列表,该活动有一个场景列表,而该场景有一个冒险列表。

Users -> Lcampaings -> Lscenaries -> Ladventures用户 -> Lcampaings -> Lscenaries -> Ladventures

Each campaign, scenary or adventure, have resources which contains the list of documents, images, resources etc, and a hashset of summaries.每个活动、场景或冒险都有资源,其中包含文档、图像、资源等列表以及摘要的哈希集。

Campaign/Scenary/Adventure -> Resources -> Ldocuments/LImages/.../HashSet Summaries战役/场景/冒险 -> 资源 -> Ldocuments/LImages/.../HashSet 摘要

ok, so to modify the summaries i have implemented equality and gethashcode好的,所以要修改摘要,我已经实现了相等和 gethashcode

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Windows;

namespace ElEscribaDelDJ.Classes
{
    public class Resumenes: INotifyPropertyChanged, IEqualityComparer<Resumenes>
    {

        private string _nombre;

        public string Nombre
        {
            get { return _nombre; }
            set { 
                _nombre = value;
                OnPropertyChanged("Nombre");
            }
        }

        private string _etiquetas;

        public string Etiquetas
        {
            get { return _etiquetas; }
            set { 
                _etiquetas = value;
                OnPropertyChanged("Etiquetas");
            }
        }

        private string _descripcion;

        public string Descripcion
        {
            get { return _descripcion; }
            set { 
                _descripcion = value;
                OnPropertyChanged("Descripcion");
            }
        }

        private int _pagina;

        public int Pagina
        {
            get { return _pagina; }
            set {
                if (value <= 0) 
                    _pagina = 1;
                else
                    _pagina = value;
                OnPropertyChanged("Pagina");
            }
        }

        private string _manual;

        public string Manual
        {
            get { return _manual; }
            set { 
                _manual = value;
                OnPropertyChanged("Manual");
            }
        }

        private string _manualurl;

        public string ManualUrl
        {
            get { return _manualurl; }
            set
            {
                _manualurl = value;
                OnPropertyChanged("ManualUrl");
            }
        }

        private string _tipoaventura;

        public string TipoAventura
        {
            get { return _tipoaventura; }
            set { 
                _tipoaventura = value;
                OnPropertyChanged("TipoAventura");
            }
        }

        private string _nombretipoaventura;

        public string NombreTipoAventura
        {
            get { return _nombretipoaventura; }
            set {
                _nombretipoaventura = value;
                OnPropertyChanged("NombreTipoAventura");
            }
        }


        private int _indice;

        public int Indice
        {
            get { return _indice; }
            set
            {
                _indice = value;
                OnPropertyChanged("Indice");
            }
        }

        private List<int> _indiceslibres;

        public List<int> IndicesLibres
        {
            get { return _indiceslibres; }
            set
            {
                _indiceslibres = value;
                OnPropertyChanged("IndicesLibres");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string PropertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }

        public bool Equals(Resumenes x, Resumenes y)
        {
            if (x.Nombre.Equals(y.Nombre) && x.Descripcion.Equals(y.Descripcion))
                return true;
            else
                return false;
        }

        public int GetHashCode(Resumenes obj)
        {
            MessageBox.Show("El Hash code es " + obj.Nombre.GetHashCode());
            return obj.Nombre.GetHashCode();
        }

        public Resumenes CopiarValores ()
        {
            return (Resumenes)this.MemberwiseClone();
        }
    }
}

(In gethashcode i have the messagebox just to know if was called ofc i know it shouldnt be there) (在 gethashcode 我有消息框只是为了知道是否被称为 ofc 我知道它不应该在那里)

I am using the name and description of two objects to know if they are equally or not, and for gethashcode the name.我正在使用两个对象的名称和描述来了解它们是否相等,并使用 gethashcode 的名称。

I have done this after read a lot of questions about how it works hashcode and equallity, hashcodeA == hashcodeB means they could be equally so name looks like perfect for that and thats why in equallity i use also description, because if you have same name and same description its mostly the same summary.在阅读了很多关于哈希码和相等性的工作原理的问题后,我已经这样做了,hashcodeA == hashcodeB 意味着它们可以相同,所以名称看起来很完美,这就是为什么在平等中我也使用描述,因为如果你有相同的名字和相同的描述它的摘要大致相同。

Ok, so i show a list of all summaries, the user select one, click edit, in the windows for add or edit i do a not in deep copy of the objects and after that i call for example campaign edit summary, where i delete the old object and add the new one, because i readed that's the best way if you have modified the fields used to make the hashcode.好的,所以我显示了所有摘要的列表,用户 select 一个,单击编辑,在 windows 中进行添加或编辑,我在对象的深层副本中不做一个,然后我调用例如活动编辑摘要,我删除旧的 object 并添加新的,因为我读到如果您修改了用于制作哈希码的字段,这是最好的方法。

public void EditarResumen(Resumenes resumenviejo, Resumenes resumennuevo)
        {
            DatosAplicacion.CampanaSeleccionada.Recursos.Resumenes.Remove(resumenviejo);
            DatosAplicacion.CampanaSeleccionada.Recursos.Resumenes.Add(resumennuevo);
            RecursosAplicacion.SesionUsuario.ReemplazarCampana();
        }

"Datosaplicacion" is a static class which have the campaign, scenary and aventure that the users chose from all of them “Datosaplicacion”是一个static class,其中有用户选择的活动,场景和冒险

using System;
using System.Collections.Generic;
using System.Text;

namespace ElEscribaDelDJ.Classes.Utilidades.Aplicacion
{
    public static class DatosAplicacion
    {
        private static Campana _campana = new Campana();

        public static Campana CampanaSeleccionada
        {
            get { return _campana; }
            set { _campana = value; }
        }

        public static int IndiceCampana;

        private static EscenarioCampana _escenarioseleccionado = new EscenarioCampana();

        public static EscenarioCampana EscenarioSeleccionado
        {
            get { return _escenarioseleccionado; }
            set { _escenarioseleccionado = value; }
        }

        public static int IndiceEscenario;

        private static Aventura _aventuraseleccionada;

        public static Aventura AventuraSeleccionada
        {
            get { return _aventuraseleccionada; }
            set { _aventuraseleccionada = value; }
        }

        public static int IndiceAventuraSeleccionada;
    }
}

resumenviejo (oldsummary) is made with resumenviejo (oldsummary) 是用

public Resumenes CopiarValores ()
        {
            return (Resumenes)this.MemberwiseClone();
        }

this should be fine because i dont have any reference object or similar.这应该没问题,因为我没有任何参考 object 或类似的。

But when i debugg the application the remove operation always throw false, and never calls the equality operation neither the gethashcode.但是当我调试应用程序时,删除操作总是抛出错误,并且永远不会调用相等操作,也不会调用 gethashcode。

And i don't know what is happening.而且我不知道发生了什么。

I used this article to do the operations https://dotnetcodr.com/2017/01/12/how-to-check-whether-two-hashsets-are-equal-in-c-net-2/#:~:text=Two%20HashSet%20objects%20in%20C#,their%20order%20in%20the%20collection .我用这篇文章做了操作https://dotnetcodr.com/2017/01/12/how-to-check-whether-two-hashsets-are-equal-in-c-net-2/#:~: text=Two%20HashSet%20objects%20in%20C#,their%20order%20in%20the%20collection

I have the full code uploaded to github https://github.com/davidgmd/Proyecto-de-fin-de-grado我已将完整代码上传到 github https://github.com/davidgmd/Proyecto-de-fin-de-grado

You have two methods GetHashCode and Equals您有两种方法GetHashCodeEquals

public bool Equals(Resumenes x, Resumenes y)

public int GetHashCode(Resumenes obj)

But they are not overriding the correct methods from the framework so they won't be called.但是它们没有覆盖框架中的正确方法,因此不会被调用。 You have to override the following to methods, so that they will be used by the framework您必须覆盖以下方法,以便框架使用它们

public override bool Equals(object obj) {
  if (!(obj is Resumenes)) return false;
  var other = obj as Resumenes;
  return this.Nombre.Equals(other.Nombre) && this.Descripcion.Equals(other.Descripcion);
}

public override int GetHashCode() {
   return this.Nombre.GetHashCode();
}

Note, that this is not really needed.请注意, this并不是真正需要的。 It's just to clarify that this instance is compared with the other object passed in.只是为了澄清一下, this实例是与传入的other object 进行比较的。

EDIT编辑

You can use your overriding of IEqualityComparer<Resumenes> but then you will have to pass it to the constructor of the hashset.您可以使用IEqualityComparer<Resumenes>的覆盖,但是您必须将其传递给哈希集的构造函数。 But it's quite uncommon for the data object you put into a HashSet to implement IEqualityComparer .但是对于您放入 HashSet 以实现IEqualityComparer的数据 object 来说,这种情况并不常见。 Better your Resumenes should implement the IEquatable<T> interface更好的简历应该实现Resumenes IEquatable<T>接口

public class Resumenes: INotifyPropertyChanged, IEquatable<Resumenes> {


    public override bool Equals(object obj) { ... }
    public bool Equals(Resumenes other) { ... }
    public override int GetHashCode() { ... }
}

There are a few things here:这里有几件事:

  1. since Nombre is effectively the hash-key, if it changes while the item is in the hash: all bets are off;由于Nombre实际上是哈希键,如果它在项目位于 hash 中时发生变化:所有赌注都关闭; a simple way to avoid that is to make it read-only避免这种情况的一种简单方法是将其设为只读
  2. it is very odd to have a leaf type implement IEqualityComparer<T> ;有一个叶子类型实现IEqualityComparer<T>是很奇怪的; I wonder if this is a large part of the problem - especially if you haven't passed a explicit comparer into the hash-set;我想知道这是否是问题的很大一部分——尤其是如果您没有将显式比较器传递到哈希集中; however, honestly, it would be simpler and preferable to implement IEquatable<T> here:但是,老实说,在这里实现IEquatable<T>会更简单,更可取:
public class Resumenes : INotifyPropertyChanged, IEquatable<Resumenes>
{
        // ...
        public override bool Equals(object obj) => obj is Resumenes other && Equals(other);
        public bool Equals(Resumenes other)
            => other is not null && other.Nombre == this.Nombre && other.Descripcion == this.Descripcion;

        public override int GetHashCode()
            => Nombre.GetHashCode();
}

You can do this with a custom equality comparer, but you'd need to explicitly pass such a comparer into the new HashSet<Resumenes>(comparer) constructor.可以使用自定义相等比较器来执行此操作,但您需要将此类比较器显式传递给new HashSet<Resumenes>(comparer)构造函数。 I would expect this comparer to be a singleton instance of a different type , for example ResumenesComparer.Instance .我希望这个比较器是一个不同类型的 singleton 实例,例如ResumenesComparer.Instance Using IEquatable<T> is far more obvious and convenient.使用IEquatable<T>更加明显和方便。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM