C# 为什么在调用列表上的排序(IComparer)时会得到System.ArgumentException?

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

我用自己的IComparer对列表进行排序,当运行应用程序(XNA游戏)超过一个小时时,这一切都很好。但是,在使用自定义比较器调用sort方法时,有时会突然出现以下错误:

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;
    }