2011-05-31 34 views
6

mayoría de la gente, cuando se escribe un tipo refence (clase) que implementa IComparable <T>, utilice la convención de que NULL es menos que cualquier objeto real. Pero si intenta utilizar la convención contrario, sucede algo muy interesante:IComparable algunos de los cuales son nulos

using System; 
using System.Collections.Generic; 

namespace SortingNulls 
{ 
    internal class Child : IComparable<Child> 
    { 
    public int Age; 
    public string Name; 

    public int CompareTo(Child other) 
    { 
     if (other == null) 
     return -1; // what's your problem? 

     return this.Age.CompareTo(other.Age); 
    } 

    public override string ToString() 
    { 
     return string.Format("{0} ({1} years)", this.Name, this.Age); 
    } 
    } 

    internal static class Program 
    { 
    private static void Main() 
    { 
     var listOfChilds = new List<Child> 
     { 
     null, 
     null, 
     null, 
     null, 
     new Child { Age = 5, Name = "Joe" }, 
     new Child { Age = 6, Name = "Sam" }, 
     new Child { Age = 3, Name = "Jude" }, 
     new Child { Age = 7, Name = "Mary" }, 
     null, 
     null, 
     null, 
     null, 
     new Child { Age = 7, Name = "Pete" }, 
     null, 
     new Child { Age = 3, Name = "Bob" }, 
     new Child { Age = 4, Name = "Tim" }, 
     null, 
     null, 
     }; 

     listOfChilds.Sort(); 

     Console.WriteLine("Sorted list begins here"); 
     for (int i = 0; i < listOfChilds.Count; ++i) 
     Console.WriteLine("{0,2}: {1}", i, listOfChilds[i]); 
     Console.WriteLine("Sorted list ends here"); 
    } 
    } 
} 

Cuando se ejecuta el código anterior, se ve que las referencias nulas no se ordenan como se esperaba. Aparentemente, cuando se compara A a B, si A es un objeto y B es nulo, se usa la comparación definida por el usuario, pero si por el contrario A es nula y B es un objeto, en su lugar se usa alguna comparación BCL.

¿Esto es un error?

Respuesta

8

No, esto no es un error. Su método CompareTo que implementa IComparable<Child> se define en su clase Child. En otras palabras, si tiene que invocar un método en uno de sus tipos para hacer una comparación.

Si uno de los artículos Child que se está comparando es nulo, ¿cómo puede invocar CompareTo en él?

Tenga en cuenta que a partir de la definición de IComparable:

"Por definición, cualquier objeto compara mayor que (o sigue) una referencia nula (Nothing en Visual Basic), y dos referencias nulas comparan iguales entre sí. "

Lo que explica los resultados que observa.

La solución es delegar en alguna otra clase para realizar la comparación. Vea la interfaz IComparer.

+0

Nice answers. No había notado que la documentación decía que un objeto debe comparar mayor que nulo. Cuando A es nula y B no es nulo, el marco podría * * han intercambiado A y B, llamado a mi método, y luego se volcó la señal de la Int32 devuelto. Creo que a veces sucede con el viejo IComparable no genérico. Por supuesto con un IComparer o un delegado Comparación es fácil de manejar nulo como el primer parámetro. – JeppeSN

1

¿Qué esperar que suceda si se trató de evaluar si this.Age.CompareTo(other.Age);this es null? De hecho, this nunca puede ser null en C#.

En cuanto a preguntar si es un error, vea this blog post.

1

El Comparer<T> predeterminado para un tipo T tiene que tener en cuenta el escenario donde el primer elemento (llamémoslo A) es null. Digamos que es como la siguiente:

if (ReferenceEquals(a, null)) 
{ 
    return -1; 
} 

return a.CompareTo(b); 

Esto se basa en the documentation of List<T>.Sort:

Este método utiliza el comparador predeterminado Comparer(Of T).Default para el tipo T para determinar el orden de los elementos de la lista.

Podría decirse que el escalón más alto sólo podía volver 0 si ambos elementos son null, y de otra manera utilizar el opuesto de b.CompareTo(a).

No lo llamaría realmente un error , sin embargo. Es solo algo de lo que debes estar consciente.

1

No, es el código que tiene un "error" ya que no es siguiendo las normas que definen IComparable.CompareTo(): IComparable

Específicamente: Por definición, cualquier objeto compara mayor que (o sigue) null, y dos null las referencias se comparan entre sí.

En su ejemplo usted está definiendo su objeto para comparar más pequeño que (o precede) null, que es precisamente lo contrario de cómo debería hacerse.

Cuestiones relacionadas