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
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
ok, so to modify the summaries i have implemented equality and 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)
I am using the name and description of two objects to know if they are equally or not, and for gethashcode the name.
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.
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.
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
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
public Resumenes CopiarValores ()
{
return (Resumenes)this.MemberwiseClone();
}
this should be fine because i dont have any reference object or similar.
But when i debugg the application the remove operation always throw false, and never calls the equality operation neither the 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.
I have the full code uploaded to github https://github.com/davidgmd/Proyecto-de-fin-de-grado
There are a few things here:
Nombre
is effectively the hash-key, if it changes while the item is in the hash: all bets are off; a simple way to avoid that is to make it read-onlyIEqualityComparer<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: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. I would expect this comparer to be a singleton instance of a different type, for example ResumenesComparer.Instance
. Using IEquatable<T>
is far more obvious and convenient.