¿Cuándo debo usar el operador de conversión de tipo implícito de C #?

14

En C #, podemos sobrecargar el operador de conversión implícito como este (ejemplo de MSDN ):

struct Digit
{
    /* ... */
    public static implicit operator byte(Digit d)  // implicit digit to byte conversion operator
    {
        /* ... */
    }
}

Por lo tanto, podemos tener un tipo, un tipo de valor personalizado , que se convierte mágicamente a otro tipo (no relacionado), dejando a la audiencia desconcertada (hasta que se asoman al backstage y ven el operador de conversión implícito).

No me gusta dejar a nadie que lea mi código con desconcierto. No creo que mucha gente lo haga.

La pregunta es, ¿cuáles son los casos de uso del operador de conversión de tipo implícito que no harán que mi código sea mucho más difícil de entender?

Mentas97
fuente
1
Guau. En realidad no sabía que esto existía. No es que sea necesariamente una buena cosa para usar; Sé que la gente se molestó mucho por este tipo de funcionalidad que oculta en C ++.
Katana314
@ Katana314: Eso no fue por lo que la gente se molestó, sino por alguien que agregó una sobrecarga (ya sea operador, función de conversión, constructor, función libre o función miembro) con un comportamiento sorprendente, preferiblemente sutilmente sorprendente.
Deduplicador
Le recomiendo que lea sobre "sobrecarga de operadores" en C ++, específicamente los operadores de "conversión". Sospecho que muchos de los mismos argumentos a favor o en contra son los mismos, excepto que el debate ha estado ocurriendo tres veces siempre que C # haya existido con mucho más para leer.

Respuestas:

18

Solo recomendaría conversiones implícitas entre tipos que representan aproximadamente los mismos valores de diferentes maneras. Por ejemplo:

  • Los diferentes tipos de color como RGB, HSL, HSVy CMYK.
  • Diferentes unidades para la misma cantidad física ( Metervs Inch).
  • Diferentes sistemas de coordenadas (polar vs cartesiano).

Sin embargo, hay algunas pautas sólidas que indican cuando es no conveniente definir una conversión implícita:

  • Si la conversión causa una pérdida significativa de precisión o rango, entonces no debería ser implícita (por ejemplo: de float64 a float32 o de long a int).
  • Si la conversión puede arrojar una InvalidCastexcepción ( ), entonces no debería ser implícita.
  • Si la conversión causa una asignación de montón cada vez que se realiza, entonces no debería ser implícita.
  • Si la conversión no es una O(1)operación, entonces no debería ser implícita.
  • Si el tipo de origen o el tipo de destino es mutable, entonces la conversión no debería ser implícita.
  • Si la conversión depende de algún tipo de contexto (base de datos, configuración cultural, configuración, sistema de archivos, etc.), entonces no debería ser implícita (en este caso, también desalentaría un operador de conversión explícito).

Ahora digamos que su operador de conversión f: T1 -> T2no viola ninguna de las reglas anteriores, entonces el siguiente comportamiento indica fuertemente que la conversión puede ser implícita:

  • Si a == bentonces f(a) == f(b).
  • Si a != bentonces f(a) != f(b).
  • Si a.ToString() == b.ToString()entonces f(a).ToString() == f(b).ToString().
  • Etc. para otras operaciones que se definen en ambos T1y T2.
Elian Ebbing
fuente
Todos sus ejemplos son probablemente con pérdida. Si son lo suficientemente exactos de todos modos, ...
Deduplicador
Sí, me di cuenta de eso :-). No podría pensar en un mejor término para "con pérdida". Lo que quise decir con "pérdida" son conversiones donde el rango o la precisión se reduce sustancialmente. Por ejemplo, de float64 a float32 o de long a int.
Elian Ebbing
Creo que a! = B => f (a)! = F (b), probablemente no debería aplicarse. Hay muchas funciones que pueden devolver el mismo valor para diferentes entradas, floor () y ceil (), por ejemplo en el lado matemático
cdkMoose
@cdkMoose Tienes razón, por eso, veo estas propiedades más como "puntos de bonificación", no como reglas. La segunda propiedad simplemente significa que la función de conversión es inyectiva. Este suele ser el caso cuando convierte a un tipo que tiene un rango estrictamente mayor, por ejemplo, de int32 a int64.
Elian Ebbing
@cdkMoose Por otro lado, la primera propiedad solo indica que dos valores dentro de la misma clase de equivalencia de T1(implicado por la ==relación en T1) siempre se asignan a dos valores dentro de la misma clase de equivalencia de T2. Ahora que lo pienso, supongo que la primera propiedad debería ser necesaria para una conversión implícita.
Elian Ebbing
6

La pregunta es, ¿cuáles son los casos de uso del operador de conversión de tipo implícito que no harán que mi código sea mucho más difícil de entender?

Cuando los tipos no están relacionados (a los programadores). Hay escenarios (raros) en los que tiene dos tipos no relacionados (en lo que respecta al código), que están realmente relacionados (en lo que respecta al dominio o programadores razonables).

Por ejemplo, algún código para hacer coincidir cadenas. Un escenario común es hacer coincidir un literal de cadena. En lugar de llamar IsMatch(input, new Literal("some string")), una conversión implícita le permite deshacerse de esa ceremonia, el ruido en el código, y enfocarse en el literal de cadena.

La mayoría de los programadores verán IsMatch(input, "some string")e intuirán rápidamente lo que está sucediendo. Hace que su código sea más claro en el sitio de la llamada. En resumen, hace que sea un poco más fácil entender lo que está sucediendo, a un ligero costo de cómo está sucediendo.

Ahora, podría argumentar que una simple sobrecarga de funciones para hacer lo mismo sería mejor. Y es. Pero si este tipo de cosas es omnipresente, entonces tener una conversión es más limpia (menos código, mayor consistencia) que hacer un montón de sobrecargas de funciones.

Y podría argumentar que es mejor requerir que los programadores creen explícitamente el tipo intermedio para que vean "lo que realmente está sucediendo". Eso es menos sencillo. Personalmente, creo que el ejemplo de coincidencia de cadena literal es muy claro sobre "lo que realmente está sucediendo": el programador no necesita conocer la mecánica de cómo sucede todo. ¿Sabe cómo se ejecutan todos los códigos en los diversos procesadores en los que se ejecuta? Siempre hay una línea de abstracción donde los programadores dejan de preocuparse por cómo funciona algo. Si cree que los pasos de conversión implícita son importantes, no utilice la conversión implícita. Si crees que son solo una ceremonia para mantener feliz a la computadora, y sería mejor que el programador no viera ese ruido en todas partes,

Telastyn
fuente
Su último punto puede y debe llevarse aún más lejos: también hay una línea más allá de la cual un programador no debería importarle cómo se hace algo, porque eso no es contractual.
Deduplicador