C# 为什么在调用列表上的排序(IComparer)时会得到System.ArgumentException?
我用自己的IComparer对列表进行排序,当运行应用程序(XNA游戏)超过一个小时时,这一切都很好。但是,在使用自定义比较器调用sort方法时,有时会突然出现以下错误:C# 为什么在调用列表上的排序(IComparer)时会得到System.ArgumentException?,c#,list,sorting,exception,C#,List,Sorting,Exception,我用自己的IComparer对列表进行排序,当运行应用程序(XNA游戏)超过一个小时时,这一切都很好。但是,在使用自定义比较器调用sort方法时,有时会突然出现以下错误: An unhandled exception of type 'System.ArgumentException' occured in mscorlib.dll Additional Information: ArgumentException 这是引发异常的行: List<Continent> markets
An unhandled exception of type 'System.ArgumentException' occured in mscorlib.dll
Additional Information: ArgumentException
这是引发异常的行:
List<Continent> markets = new List<Continent>();
// filling the markets list ...
markets.Sort(new MarketCostCoverComparer(this));
List markets=新列表();
//填写市场清单。。。
排序(new MarketCostCoverComparer(this));
这是我实现IComparer接口的类:
class MarketCostCoverComparer : IComparer<Continent> {
private Player player;
public MarketCostCoverComparer(Player player) {
this.player=player;
}
public int Compare(Continent c1, Continent c2) {
if(player.GetCostCovering(c1)<player.GetCostCovering(c2)) {
return +1;
} else if(player.GetCostCovering(c1)==player.GetCostCovering(c2)) {
return 0;
} else {
return -1;
}
}
}
class MarketCostCoverComparer:IComparer{
私人玩家;
公共市场CostCoverComparer(玩家){
这个。玩家=玩家;
}
公共整数比较(大陆c1,大陆c2){
如果(player.getCostCoverage(c1)1=>良好
if(GetOilfieldTheoreticOutput(contraction.Type,true)如果我没记错的话,IComparer.Compare应该返回小于零的值,如果第一个参数小于第二个参数。您在实现中似乎做了相反的操作,这可以解释异常。我无法解释为什么它在失败之前运行了很长时间…问题是您的IComparer实现。它可能返回不一致的结果,因此sort函数将抛出异常
也许可以看一看和了解更多信息
问题:
地产大陆.Economy.CurrentDemand
和大陆.Economy.CurrentPrice
是否没有副作用
备注:
您的IComparer应该能够处理null
允许将null与任何类型进行比较,并且不会生成
使用IComparable时出现异常。排序时,null被视为
比任何其他物体都小
也许这是一个浮点问题,但这只是一个猜测。因此,也许你应该使用decimal
而不是float
这是我从Steve那里得到的另一个答案(来自XNA论坛):
显然,当两个对象都相同时,如果不返回0,就会出现这种情况。当两个对象都引用同一对象时,GetCostCoverage(c1)是否有可能在任何时候都不等于GetCostCoverage(c2)
为了安全起见,尝试将if(c1==c2)返回0;放在Compare方法的开头,看看结果如何
非常感谢Steve!您能提供异常的stacktrace吗?1)发布内部异常(如果有),或者至少发布堆栈跟踪2)发布GetCostCoverage()方法不幸的是,没有堆栈跟踪,因为Visual Studio只是弹出一个带有上述两行的消息框方法如下…您是否从另一个线程更改此列表?因此,在排序时,您从另一个线程删除/添加元素?好的,现在当您单击“断开”时,您可以在局部变量窗口中展开$exception
对象。或者,应该显示托管调试助手的窗口(假定它是为ArgumentException
启用的),指向触发异常的行,允许检查堆栈跟踪。但是提供IComparer.Compare
的全部目的是为了定义什么“第一个参数小于第二个参数”意思是。在这个实现中,使用了一个特定的代价函数,并且最终的比较使用了不同的运算符,这一点在这里和那里都不存在。你是对的,我承认我没有像我应该读的那样仔细阅读代码。尽管如此,在查看IComparer的文档时,它指出如果:“x和y都没有实现IComparable接口。”进一步说:“首选的实现是使用其中一个参数的CompareTo方法。“因此,我建议您在本例中查看一下Continental类,并实现CompareTo。您自己并不实现compare方法-至少您不必实现。GetCostCoverage返回浮点值,因此comparer可以是return player.GetCostCoverage(c1)。CompareTo(player.GetCostCoverage(c2))
,这使它变得简单了一点,但并不能解决OPs问题。@salocinx:这是一个多线程应用程序吗?在排序过程中,worldmap.Continents
中的值可能会发生变化吗?他在某个地方写道,到目前为止它是单线程的。我确信堆栈跟踪将提供更深入的信息,为什么比较器会返回不一致的结果?要么一个值与自身比较不相等,要么一个值与另一个值重复比较产生不同的结果results@Filip:我同意,这应该重新表述为“问题可能在于您的getcostcoverage
的实现。”。我怀疑浮点数与其自身不相等,因此假设计算是确定性的,并且值在排序过程中不会改变,我看不出这段代码有明显的问题。新添加的屏幕截图显示,异常是在System.Collections.Generic.ArraySortHelper中引发的;并且该类引发ArgumentEx的唯一方式是Option是指当快速排序实现引发IndexOutOfRangeException时;如果比较器以某种方式损坏,则会发生这种情况。因此,建议是他的比较器以某种方式损坏?我感觉OP已经知道了这一点。此外,ArgumentException可能会在未显示的代码中的任何其他地方出现(例如,所有属性)。如果这两个参数引用同一个对象,并且在一行中调用两次时,getcostcoverage
返回不同的结果,那么您需要调试它,而不是隐藏它。
public float GetCostCovering(Continent continent) {
// cover<1 => bad | cover>1 => good
if(GetOilfieldTheoreticOutput(continent.Type, true)<continent.Economy.CurrentDemand) {
return ((float)((GetOilfieldTheoreticOutput(continent.Type, true)*continent.Economy.CurrentPrice)))/(float)GetOilfieldCosts(continent.Type, true);
} else {
return ((float)((continent.Economy.CurrentDemand*continent.Economy.CurrentPrice)))/(float)GetOilfieldCosts(continent.Type, true);
}
}
public int GetOilfieldTheoreticOutput(ContinentType continent, bool drilled) {
int total = 0;
foreach(Oilfield oilfield in worldmap.Continents[(int)continent].Oilfields) {
if(oilfield.Owner==this && oilfield.Drilled==drilled) {
total+=oilfield.TheoreticOutput;
}
}
return total;
}
public int GetOilfieldCosts(ContinentType continent, bool drilled) {
int total = 0;
foreach(Oilfield oilfield in worldmap.Continents[(int)continent].Oilfields) {
if(oilfield.Owner==this && oilfield.Drilled==drilled) {
total+=oilfield.Costs;
}
}
return total;
}