C# 带有自定义GetHashCode的ErrorProvider

C# 带有自定义GetHashCode的ErrorProvider,c#,winforms,hash,gethashcode,errorprovider,C#,Winforms,Hash,Gethashcode,Errorprovider,我有一个表格负责创建(和保存)新患者。在此表单上,我使用ErrorProvider在无效字段上显示错误图标(在本例中仅显示“LastName”)。所以,像往常一样=>errorProvider.DataSource=patient 当我的模型使用默认的GetHashCode()时,一切正常。但是,当我尝试使用自定义哈希代码重写此方法时(我希望将此模型用于ISet集合),该控件无法正常工作。现在,我知道自定义哈希代码应该只用于不可变对象。但问题是,如果ErrorProvider行为依赖GetHas

我有一个表格负责创建(和保存)新患者。在此表单上,我使用ErrorProvider在无效字段上显示错误图标(在本例中仅显示“LastName”)。所以,像往常一样=>errorProvider.DataSource=patient

当我的模型使用默认的GetHashCode()时,一切正常。但是,当我尝试使用自定义哈希代码重写此方法时(我希望将此模型用于ISet集合),该控件无法正常工作。现在,我知道自定义哈希代码应该只用于不可变对象。但问题是,如果ErrorProvider行为依赖GetHashCode来正常工作,我如何填充这些对象的字段?是否有必要实现在默认哈希代码(在对象初始化期间)和自定义哈希代码之间切换的脏机制

代码示例:

public class Patient : IDataErrorInfo, INotifyPropertyChanged
{
    public string lastName;

    public virtual string LastName
    {
        get { return lastName; }
        set
        {
            if (lastName == value) return;
            lastName = value;
            NotifyPropertyChanged("LastName");

        }
    }

    #region IDataErrorInfo Members

    string IDataErrorInfo.Error { get { return null; } }

    string IDataErrorInfo.this[string propertyName]
    {
        get { return this.GetValidationError(propertyName); }
    }

    #endregion // IDataErrorInfo Members


    protected string GetValidationError(string propertyName)
    {
            if (ValidatedProperties.IndexOf(propertyName) < 0)
                return null;

            string error = null;

            switch (propertyName)
            {
                case "LastName": 
                    if (LastName == null)
                        error = "null";
                    break;

                default:
                    break;
            }

            return error;
    }

    public virtual event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int result = 17;
            result = 23 * result + ((LastName != null) ? LastName.GetHashCode() : 0);
            return result;
        }
    }
}
公共类患者:IDataErrorInfo,InotifyProperty已更改
{
公共字符串lastName;
公共虚拟字符串LastName
{
获取{return lastName;}
设置
{
if(lastName==value)返回;
lastName=值;
NotifyPropertyChanged(“姓氏”);
}
}
#区域IDataErrorInfo成员
字符串IDataErrorInfo.Error{get{return null;}}
字符串IDataErrorInfo。此[string propertyName]
{
获取{返回this.GetValidationError(propertyName);}
}
#endregion//IDataErrorInfo成员
受保护的字符串GetValidationError(字符串属性名称)
{
if(ValidatedProperties.IndexOf(propertyName)<0)
返回null;
字符串错误=null;
交换机(propertyName)
{
案例“LastName”:
if(LastName==null)
error=“null”;
打破
违约:
打破
}
返回误差;
}
公共虚拟事件属性ChangedEventHandler属性Changed;
受保护的void NotifyPropertyChanged(字符串propertyName)
{
if(PropertyChanged!=null)
PropertyChanged(这是新的PropertyChangedEventArgs(propertyName));
}
公共覆盖int GetHashCode()
{
未经检查
{
int结果=17;
result=23*result+((LastName!=null)?LastName.GetHashCode():0);
返回结果;
}
}
}

函数中使用的每个字段必须是不可变的。我不建议实现两个版本的
GetHashCode
,因为它应该是持久的和可重复的。我知道解决这个问题的一种可能的方法。若您知道某些对象将被更改,则可以在编辑操作之前将其从集合中删除,并在所有修改完成后再次添加到集合中。在这种情况下,您可以跳过对
GetHashCode
的重写,并与实现接口的指定比较器一起使用

更新

通常,如果结果不需要排序集,我建议使用
HashSet
SortedSet
采用二叉搜索树,似乎没有使用
GetHashCode
函数
SortedSet
比HashSet慢一点
SortedSet
性能约为O(log n),因为它必须找到一个空间,以便在已排序的集合中插入元素。HashSet只接受O(1)

IComparer
帮助查找两个对象是否相等(无需调用
Equals
)或告诉它们中的哪一个小于或大于另一个。我写了一些代码来测试SortedSet的功能

代码
公共类Foo
{
公共Foo(字符串)
{
某物=某物;
}
公共字符串Something{set;get;}
}
公共类:IComparer
{
private readonly CaseInsensitiveComparer _comparer=new CaseInsensitiveComparer();
公共整数比较(Foo x,Foo y)
{
return\u comparer.Compare(x.Something,y.Something);
}
}
试验
[TestMethod]
公共空间分类测试()
{
var first=新Foo(“Doe”);
var second=新的Foo(“Floyd”);
第三风险值=新的Foo(“弗洛伊德”);
变量集=新的分类集(新的BySomething());
集合。添加(第一);
集合。添加(第二个);
增加(第三);
Assert.AreEqual(set.Count,2);
}

您是否尝试过重写Equals()?另外,我会在GetHashCode()内设置一个断点,并检查调用堆栈,试图找出控件是如何使用它的。很难理解ErrorProvider实际上在做什么。在ShowDialog期间,ErrorProvider似乎多次调用GetHashCode()(可能对表单上的每个控件调用一次),然后对“LastName”调用GetValidationError(string propertyName)。整个验证步骤重复了两次(为什么?)。但是如果我使用的是像nHibernate这样的ORM,那么如果我必须将新创建的对象与填充了数据库对象的SortedSet集合进行比较,该怎么办?如果没有自定义的GetHashCode(),我无法检测新对象是否实际上是现有db行的副本。它们将具有相同的业务键值,但哈希代码不同。。。因此,它将被错误地添加到SortedSet。IComparer接口在内部用于对对象进行排序,但我认为集合不会使用它来检测副本。这就是Equals()/GetHashCodes()的用途。我错过什么了吗?
public class Foo
{
    public Foo(string something)
    {
        Something = something;
    }
    public string Something { set; get; }
}

public class BySomething : IComparer<Foo>
{
    private readonly CaseInsensitiveComparer _comparer = new CaseInsensitiveComparer();
    public int Compare(Foo x, Foo y)
    {
        return _comparer.Compare(x.Something, y.Something);
    }
}
[TestMethod]
public void SortedSetTest()
{
    var first = new Foo("Doe");
    var second = new Foo("Floyd");
    var third = new Foo("Floyd");

    var set = new SortedSet<Foo>(new BySomething());
    set.Add(first);
    set.Add(second);
    set.Add(third);

    Assert.AreEqual(set.Count, 2);
}