2010-09-25 14 views
8

Quiero escribir una función genérica que tenga una restricción sobre el tipo. Específicamente quiero algo como esto:Restricciones genéricas en la función

bool IsInList<T>(T value, params T[] args) 
{ 
    bool found = false; 
    foreach(var arg in args) 
    { 
     if(arg == value) 
     { 
      found = true; 
      break; 
     } 
    } 
    return found; 
} 

El punto es que se puede comprobar si un elemento está en una lista de parámetros a saber:

if(IsInList("Eggs", "Cheese", "Eggs", "Ham")) 

Sin embargo, el compilador estira la pata en la línea de igualdad. Así que quiero poner una restricción sobre el tipo que implementa IEquatable. Sin embargo, las restricciones solo parecen funcionar en el nivel de clase. ¿Es correcto o hay alguna forma de especificar esto genéricamente?

Respuesta

7

limitaciones genéricas trabajan en métodos genéricos, así:

bool IsInList<T>(T value, params T[] args) where T : IEquatable<T> 

PERO, IEquatable<T> no define operator ==, solamente Equals(T).

Por lo tanto, debe usar Equals() y ni siquiera necesita la restricción para eso: Equals(object) es miembro de object.

Además, no olvide que Equals no funcionará si el objeto es null.

+6

Puede usar 'object.Equals (a, b)' en lugar de 'a.Equals (b)' para que no tenga que preocuparse por objetos nulos. – Servy

+1

Ahora, si solo MS actualizase su documentación para reflejar esta realidad, en lugar de dar a todos la impresión de que la restricción solo es válida para las declaraciones de clase. –

1

El operador `== 'se usa generalmente para tipos inmutables solamente (tipos de valores incorporados o tipos inmutables personalizados que han sobrecargado el operador ==. A menos que lo sobrecargue, si lo usa en tipos de referencia, (excepto cadenas) solo devuelve verdadero si ambas variables apuntan a la misma instancia del tipo (mismo objeto). Devolvería falso si los dos verificables apuntan a instancias diferentes del tipo incluso si ambos tienen todos los mismos valores de datos internos.

Necesita restringir el tipo T a los tipos que implementan la función Equals(), que está destinada a determinar si dos instancias de cualquier tipo son "iguales" y no solo que ambas apuntan a la misma instancia ... y use eso en su lugar. La interfaz integrada IEquatable<T> expresa esto para usted. (Un poco como IComparable<T> requiere que un tipo tiene una función CompareTo())
También, usted puede hacer su función mucho más concisa y clara ...

intente esto:

bool IsInList<T>(T value, params T[] args) where T:IEquatable<T> 
{ 
    foreach(var arg in args)   
     if(value.Equals(arg)) return true; 
    return false; 
} 

También debe entender la diferencia entre Equals() y == y decida cuál es más apropiado para cualquiera que sea su intención ... Consulte la referencia this para obtener más información.

+0

eso no funciona, '' IEquatable no contiene '' operador == y no es de alguna manera mágica creado tampoco. Además, 'Equals()' funciona incluso sin 'IEtabletable ' (aunque es 'Igual (objeto)' y no 'Igual (T)'). – svick

+0

Sí, pero no puede esperar que el compilador implemente el código de un método Equals para usted en cualquier tipo personalizado arbitrario que haya definido. Tienes que escribir ese código tú mismo. Esta restricción simplemente le impide llamar al método en tipos que no han implementado este método. Y, sí, también, tiene razón en que el operador == no está en IEquatable, mi error, lo edité para corregirlo ... Consulte el enlace que agregué para obtener más información. –

+0

No, no es exactamente lo mismo. La firma de 'IEquatable .Equals' no es lo mismo que 'object.Equals'. Al restringirlo de esta manera, impide que funcione en tipos en los que la comparación de igualdad predeterminada funciona perfectamente. Básicamente, desafortunadamente no hay forma de decir, "restringir T a un tipo que tiene una comparación de igualdad personalizada": su restricción actual es demasiado estrecha, y "sin restricción" es demasiado amplia. –

0

basta con sustituir

arg == value 

con

arg.Equals(value) 
11

Otros han mencionado IEquatable<T> que sin duda es una buena restricción de potencial.

Otra opción para recordar es EqualityComparer<T>.Default, que usará IEquatable<T> si está disponible, pero retroceda a object.Equals(object) de lo contrario.Esto significa que se puede utilizar con los tipos de los cuales son anteriores a los genéricos, pero anulan Equals, por ejemplo:

bool IsInList<T>(T value, params T[] args) 
{ 
    IEqualityComparer<T> comparer = EqualityComparer<T>.Default; 
    bool found = false; 
    foreach(var arg in args) 
    { 
     if(comparer.Equals(arg, value)) 
     { 
      found = true; 
      break; 
     } 
    } 
    return found; 
} 

Tenga en cuenta que el comparador de igualdad predeterminado también se enfrenta con referencias nulas, por lo que no necesita preocuparse por los mismo. Si el tipo de T no ha anulado o object.Equals(object) implementado IEquatable<T>, obtendrá la semántica de igualdad de referencia (es decir, que sólo devolverá true si la referencia exacta es en la matriz).

Algunos otros puntos:

  • su deseo de adherirse a un solo punto de salida hace que el código menos legible, la OMI. No hay necesidad de una variable adicional aquí:

    bool IsInList<T>(T value, params T[] args) 
    { 
        IEqualityComparer<T> comparer = EqualityComparer<T>.Default; 
        foreach (var arg in args) 
        { 
         if (comparer.Equals(arg, value)) 
         { 
          return true; 
         } 
        } 
        return false; 
    } 
    
  • LINQ ya contiene un método para esto, Contains, por lo que puede simplificar el código para:

    bool IsInList<T>(T value, params T[] args) 
    { 
        return args.Contains(value); 
    } 
    
  • Array contiene efectivamente esta funcionalidad también, con IndexOf:

    bool IsInList<T>(T value, params T[] args) 
    { 
        return Array.IndexOf(args, value) != -1; 
    } 
    
  • Su método es tal vez una litt le nombre engañoso, dado que args es una matriz, no un List<T>.

+0

+1 para la respuesta muy detallada, pero creo que tiene un error tipográfico en su primer código: if (comparer.Equals (arg == value)) - Creo que debería ser if (comparer.Equals (arg, value)) . – ShdNx

+0

@ShdNx, estoy de acuerdo, espero que a Jon no le importe la edición. ;) –

+0

@ShdNx: Whoops, absolutamente :) –

Cuestiones relacionadas