2012-06-08 13 views
5

que ordenar una lista con mi propia IComparer y esto funciona bien cuando se ejecuta la aplicación (juego de XNA) durante más de una hora. Pero entonces, de repente, a veces me sale el siguiente error al invocar la clase método con mi costumbre Comparer:¿Por qué obtengo un System.ArgumentException cuando se invoca Sort (IComparer) en una lista?

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

Ésta es la línea en la que se produce la excepción:

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

y este es mi clase que implementa la interfaz 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; 
     } 
    } 

} 

Aquí algunos métodos que están vinculados con el comparador ...:

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

Aquí la captura de pantalla de la excepción:

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

Aquí, un vistazo más de cerca de los locales/Pila-Trace (esto es una vieja pantalla, pero voy a tratar de reproducir esta excepción dentro de la próxima hora, para que pueda ampliar la traza):

enter image description here

+3

¿Se puede suministrar el stacktrace de la excepción? –

+0

1) publique la excepción interna, si hay una, o al menos la traza de la pila 2) publique el método GetCostCovering() – Filip

+0

Desafortunadamente no hay ningún rastro de pila, ya que Visual Studio simplemente muestra un cuadro de mensaje con las dos líneas mencionadas anteriormente. Los métodos de GetCostCovering() siguen ... – salocinx

Respuesta

3

El problema es que su implementación de su IComparer. Puede devolver resultados inconsistentes, por lo que la función de ordenación arrojará una excepción.

Tal vez echar un vistazo a this y this question para más información.

Pregunta:

son las propiedades continent.Economy.CurrentDemandcontinent.Economy.CurrentPrice y libre de efectos secundarios?

Observaciones:

Su IComparer debe ser capaz de manejar nula. Desde el docs:

Comparando nulo con cualquier tipo que está permitido y no genera una excepción cuando se utiliza IComparable. Al ordenar, null se considera ser menor que cualquier otro objeto.

Tal vez sea un problema de coma flotante, pero eso es solo una suposición descabellada. Así que tal vez se debe utilizar en lugar de decimalfloat.

+0

¿por qué exactamente el comparador puede devolver resultados incosistentes? – Filip

+0

O bien un valor no se compara igual a sí mismo, o un valor en repetidas ocasiones en comparación con otro valor arroja resultados diferentes – sloth

+1

@Filip: Estoy de acuerdo, esto debería reformularse como "El problema ** podría ** ser con su implementación de **' GetCostCovering' ** ". Dudo que un flotador no se compare a sí mismo, así que suponiendo que el cálculo es determinista y los valores no cambian durante la clasificación, no veo un problema aparente con este código. – Groo

-1

Si no recuerdo mal, IComparer.Compare deben regresar menor que cero si el primer argumento es menor que el segundo argumento. Parece que haces lo contrario en tu implementación, lo que explicaría la excepción. No puedo explicar por qué, aunque se está trabajando desde hace mucho tiempo antes de que este fracaso ...

+2

Pero todo el propósito de proporcionar 'IComparer.Compare' es para que pueda definir qué" primer argumento menos que segundo argumento "* significa *. El hecho de que, en esta implementación, se use una función de costo particular, y la comparación final use un operador diferente tampoco está aquí. –

+1

Tienes razón, admito que no estaba leyendo el código tan cuidadosamente como debería. Sin embargo, mirando la documentación de IComparer indica que se lanza una excepción ArgumentException si: "Ni x ni y implementa la interfaz IComparable". y más abajo: "La implementación preferida es usar el método CompareTo de uno de los parámetros". Así que aconsejaría echar un vistazo a la clase Continent en este caso, e implementar CompareTo. – ekholm

+1

Usted no implementa el método de comparación usted mismo, al menos no tiene que hacerlo. GetCostCovering devuelve float, por lo que el comparador puede ser 'return player.GetCostCovering (c1) .CompareTo (player.GetCostCovering (c2))', lo que lo hace un poco más simple, pero no soluciona el problema de OPs. – Filip

0

Ésta es una otra respuesta que recibí de Steve (de los foros de XNA):

Al parecer, esto puede ocurrir cuando usted no devuelve 0 para ambos objetos que son lo mismo. ¿Hay alguna posibilidad de que GetCostCovering (c1) no sea igual a GetCostCovering (c2) en cualquier momento cuando ambos hacen referencia al mismo objeto?

Por razones de seguridad, intente poner if (c1 == c2) return 0; al comienzo del método Comparar y ver cómo funciona eso.

Muchas gracias Steve!

+0

Si estos dos parámetros hacen referencia al mismo objeto, y 'GetCostCovering' arroja resultados diferentes cuando se los llama dos veces seguidas, entonces debe depurar eso, no ocultarlo en mi humilde opinión. – Groo

Cuestiones relacionadas