2012-06-08 9 views
5

Sortuję listę z moim własnym programem IComparer i działa to dobrze podczas uruchamiania aplikacji (gra XNA) przez ponad godzinę. Ale potem, nagle, czasami pojawia się następujący błąd podczas wywoływania sortowania-metody z moim niestandardowym porównywarka:Dlaczego pojawia się wyjątek System.ArgumentException podczas wywoływania sortowania (programu IComparer) na liście?

An unhandled exception of type 'System.ArgumentException' occured in mscorlib.dll 
Additional Information: ArgumentException 

Jest to linia, gdzie jest wyjątek:

List<Continent> markets = new List<Continent>(); 
// filling the markets list ... 
markets.Sort(new MarketCostCoverComparer(this)); 

i to jest mój klasa implementująca interfejs 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; 
     } 
    } 

} 

Oto kilka metod, które są związane z comparer ...:

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

Oto zrzut ekranu z wyjątkiem:

An unhandled exception of type 'System.ArgumentException' occured in mscorlib.dll

Oto bliższe spojrzenie z mieszkańcami/Stack śledzenia (jest to stara zrzut ekranu, ale postaram się odtworzyć ten wyjątek w ciągu następnych godzin, tak że mogę rozwinąć śladu):

enter image description here

+3

Czy możesz podać stacktrace wyjątku? –

+0

1) opublikować wyjątek wewnętrzny, jeśli istnieje jeden, lub przynajmniej stos 2 ślad), należy opublikować metodę GetCostCovering() – Filip

+0

Niestety nie ma śledzenia stosu, ponieważ Visual Studio po prostu wyskakuje okienko z dwoma wyżej wymienionymi wierszami. Metody GetCostCovering() następują po ... – salocinx

Odpowiedz

3

Problem stanowi implementacja programu IComparer. Może zwrócić niespójne wyniki, więc funkcja sort rzuci wyjątek. Można uzyskać więcej informacji na temat this i this question.

Pytanie:

Czy właściwości continent.Economy.CurrentDemand i continent.Economy.CurrentPrice wolne od skutków ubocznych?

Uwagi:

Twój IComparer powinien być w stanie obsłużyć NULL. Z docs:

Porównując zerowej w przypadku każdego rodzaju jest dozwolone i nie generuje wyjątek przy użyciu IComparable. Podczas sortowania wartość null jest uważana za mniejsza niż jakikolwiek inny obiekt.

Może to kwestia zmiennoprzecinkowa, ale to tylko dzikie domysły. Więc może powinieneś użyć decimal zamiast float.

+0

dlaczego akurat komparator może uzyskać niespójne wyniki? – Filip

+0

Albo wartość nie równa się sama, albo jedna wartość wielokrotnie w porównaniu do innej wartości daje różne wyniki. – sloth

+1

@Filip: Zgadzam się, to powinno być przeformułowane jako "Problem ** może ** być z twoją implementacją **" GetCostCovering' ** ". Wątpię, aby zmiennik nie porównywał się z samym sobą, więc zakładając, że obliczenia są deterministyczne, a wartości nie zmieniają się podczas sortowania, nie widzę widocznego problemu z tym kodem. – Groo

-1

Jeśli dobrze pamiętam, IComparer.Compare powinna wrócić mniej niż zero, jeśli pierwszy argument jest mniejszy niż drugi argument. Wydaje się, że robisz coś odwrotnego w swojej implementacji, co wyjaśniałoby wyjątek. Nie potrafię wyjaśnić, dlaczego to działa przez długi czas przed tą awarią ...

+2

Ale cały cel zapewnienia 'IComparer.Compare' jest taki, że możesz zdefiniować, co" pierwszy argument mniej niż drugi argument "* oznacza *. Fakt, że w tej implementacji używana jest określona funkcja kosztu, a ewentualne porównanie używa innego operatora, nie jest tutaj. –

+1

Masz rację, przyznaję, że nie czytałem kodu tak dokładnie, jak powinienem. Mimo to, patrząc na dokumentację IComparer, stwierdza on, że generowany jest wyjątek ArgumentException, jeśli: "Ani x ani y nie implementuje interfejsu IComparable." i niżej: "Preferowaną implementacją jest użycie metody CompareTo jednego z parametrów." Więc radziłbym, aby rzucić okiem na klasę Kontynent w tym przypadku i wprowadzić CompareTo. – ekholm

+1

Nie zaimplementuj metody porównania samodzielnie - przynajmniej nie musisz. Funkcja GetCostCovering zwraca wartość float, więc porównawcą może być 'return player.GetCostCovering (c1) .CompareTo (player.GetCostCovering (c2))', co czyni go nieco prostszym, ale nie naprawia problemu z OP. – Filip

0

Jest to inna odpowiedź dostałem od Steve (z forów XNA):

Podobno może to nastąpić, gdy nie zwraca 0 dla obu obiektów jest taka sama. Czy istnieje szansa, że ​​funkcja GetCostCovering (c1) może nie być równa funkcji GetCostCovering (c2) w dowolnym momencie, gdy oba odnoszą się do tego samego obiektu?

Dla bezpieczeństwa, spróbuj wprowadzić, jeśli (c1 == c2) zwróci 0; na początku metody Porównaj i zobacz, jak to działa.

Wielkie dzięki Steve!

+0

Jeśli te dwa parametry odwołują się do tego samego obiektu, a funkcja "GetCostCovering" zwraca różne wyniki po wywołaniu dwa razy z rzędu, należy debugować, a nie ukrywać IMHO. – Groo

Powiązane problemy