问题描述
我正在做一个应用程序来管理角色扮演会话的创建,但是我在做规则摘要的部分有问题,所以大师不必每秒钟都阅读核心书,我有数据结构这样。
用户有一个战役列表,该战役有一个场景列表,场景有一个冒险列表。
用户 -> Lcampaings -> Lscenaries -> Ladventures
每个战役、场景或冒险都有资源,其中包含文档、图像、资源等列表以及摘要的哈希集。
战役/场景/冒险 -> 资源 -> Ldocuments/LImages/.../HashSet 摘要
好的,为了修改我已经实现的等式和 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();
}
}
}
(在 gethashcode 中,我有消息框只是为了知道是否被称为 ofc 我知道它不应该在那里)
我使用两个对象的名称和描述来确定它们是否相等,并使用 gethashcode 名称。
我在阅读了很多关于哈希码和相等性如何工作的问题后这样做了,hashcodeA == hashcodeB 意味着它们可以相等,所以名称看起来非常适合,这就是为什么我在相等性中也使用描述,因为如果你具有相同的名称和相同的描述,其大致相同的摘要。
好的,所以我显示所有摘要的列表,用户选择一个,单击编辑,在窗口中添加或编辑我不做对象的深层副本,然后我调用例如活动编辑摘要,我删除旧对象并添加新对象,因为我读到如果您修改了用于生成哈希码的字段,这是最好的方法。
public void EditarResumen(Resumenes resumenviejo,Resumenes resumennuevo)
{
DatosAplicacion.CampanaSeleccionada.Recursos.Resumenes.Remove(resumenviejo);
DatosAplicacion.CampanaSeleccionada.Recursos.Resumenes.Add(resumennuevo);
RecursosAplicacion.SesionUsuario.ReemplazarCampana();
}
“Datosaplicacion”是一个静态类,包含用户从中选择的活动、场景和冒险
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;
}
}
resumeviejo (oldsummary) 是用
public Resumenes copiarValores ()
{
return (Resumenes)this.MemberwiseClone();
}
这应该没问题,因为我没有任何参考对象或类似物。
但是当我调试应用程序时,remove 操作总是抛出 false,并且从不调用相等操作和 gethashcode。
我不知道发生了什么。
我用这篇文章来做操作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。
我已将完整代码上传到 github https://github.com/davidgmd/Proyecto-de-fin-de-grado
解决方法
您有两个方法 GetHashCode
和 Equals
public bool Equals(Resumenes x,Resumenes y)
public int GetHashCode(Resumenes obj)
但是它们没有覆盖框架中的正确方法,因此它们不会被调用。您必须将以下内容覆盖到方法中,以便框架使用它们
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();
}
请注意,实际上并不需要 this
。只是为了澄清 this
实例与传入的 other
对象进行比较。
编辑
您可以使用 IEqualityComparer<Resumenes>
的覆盖,但随后您必须将其传递给哈希集的构造函数。但是,您放入 HashSet 的数据对象实现 IEqualityComparer
的情况并不常见。最好你的 Resumenes
应该实现 IEquatable<T>
接口
public class Resumenes: INotifyPropertyChanged,IEquatable<Resumenes> {
public override bool Equals(object obj) { ... }
public bool Equals(Resumenes other) { ... }
public override int GetHashCode() { ... }
}
,
这里有几件事:
- 因为
Nombre
实际上是散列键,如果它在项目处于散列中时发生变化:所有赌注都关闭;避免这种情况的一种简单方法是将其设为只读 - 叶子类型实现
IEqualityComparer<T>
很奇怪;我想知道这是否是问题的很大一部分 - 特别是如果您没有将显式比较器传递到哈希集中;不过,老实说,在此处实施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();
}
您可以使用自定义相等比较器来做到这一点,但是您需要将这样的比较器显式传递到 new HashSet<Resumenes>(comparer)
构造函数中。我希望这个比较器是一个不同类型的单例实例,例如 ResumenesComparer.Instance
。使用 IEquatable<T>
更加明显和方便。