Duda metafísica sobre “contravarianza” en delegates

Hola… hoy voy a poner un post sobre una dudilla metafísica que me ha surgido, concretamente relativa a los delegates. Y he pensado… que mejor sitio que ponerla que aquí??? 😉

Los delegates en C# 2.0 son contravariantes, es decir un delegate a un método que espera un parámetro tipo X aceptará cualquier método que espere un parámetro de cualquier tipo base de X.

Es decir, el siguiente código funciona bien:

delegate void Foo(Derived d);
public class Base { }
public class Derived : Base { }
public class SomeCode
{
    SomeCode()
    {
        Foo foo = new Foo(this.SomeMethod);
    }
    private void SomeMethod(Base b) { }
}

Todos entendemos la lógica que hay tras ello: al ser todos los objetos Derived, objetos Base, es evidente que cualquier método que trate con objetos Base, lo podrá hacer con objetos Derived, y por ello el método SomeMethod es un destino bueno para el delegate Foo.

Ahora bien, imaginemos que tenemos el siguiente código:

delegate void Foo(Derived d);
public class Base { }
public class Derived 
{
    public static implicit operator Base(Derived d) { return new Base(); }
}
public class SomeCode
{
    SomeCode()
    {
        Foo foo = new Foo(this.SomeMethod);
    }
    private void SomeMethod(Base b) { }
}

En este caso el código no compila, el compilador se queja con un No overload for ‘SomeCode.SomeMethod(Base)’ matches delegate ‘Foo’.

La duda metafísica es… creeis que debería compilar? En cierto (lo pongo en cursiva) todos los objetos Derived tambien son objetos Base, puesto que hay definida una conversión implícita de Derived a Base… con lo que podríamos entender que hay una cierta contravarianza.

O creeis que no? Que ya está bien que no compile puesto que el operador de conversión no puede representar nunca una relación is-a y por lo tanto la contravarianza no tiene sentido…

MMmmm… yo reconozco que no estoy 100% posicionado a favor de ninguna opción…

Saludos!

3 comentarios sobre “Duda metafísica sobre “contravarianza” en delegates”

  1. Hola, Eduard,

    Yo estoy en principio a favor de la opción 2, o sea, que no… Aunque la diferencia es sutil, el operador de conversión lo que representa es «puede verse como un…» o «puede transformarse en un…» y no «es un…».

    Mirando la última especificación del lenguaje, p. 595, parece que tu código compilaría si «exists an implicit reference conversion from Derived to Base». Tu conversión existe, es implícita y se trata de tipos referencia; entonces la conclusión parece ser que las «user-defined implicit conversions» no son consideradas como «implicit reference conversions» a estos efectos, aún cuando se definan sobre tipos referencia.

    En cualquier caso, yo en general evitaría los operadores de conversión implícitos, sobre todo con tipos referencia… Los casos que recuerdo en los que podrían ser útil precisamente involucran tipos valor (por ejemplo, en la aritmética de números complejos).

    Por cierto, parece que .NET 4.0 va a tener un tipo Complex:

    http://brad_abrams.members.winisp.net/Projects/PDC2008/PDC2008-NETFX4.pdf

  2. Hola, gracias por vuestras opiniones… 🙂

    @Bocadepez
    Es evidente que no compila y en las especificaciones del lenguaje esto debe de estar claro. No cuestiono esto. Mi pregunta (ya avisé que era metafísica) iba más en la dirección de cuestionar las propias especificaciones (no tomeis esto como un ejercicio de prepotencia, eh?). Yo mismo en el post he puesto dos visiones (una a favor de que esto compilase y otra en contra) y supongo que podrían haber más.
    Obviamente el post ha venido motivado porque me he encontrado en una situación donde «me hubiese venido bien» que algo así compilara.
    Alguien puede decirme que si necesito que esto compilase es que el diseño no está del todo correcto y probablemente tiene razón, pero todos sabemos que cosas así suceden… 😛
    Evidentemente el hecho de que a mi «me hubiese venido bien» no significa que algo debiera estar en el lenguaje… 🙂

    @Octavio
    Gracias por la referencia a las especificaciones… 😉
    Estoy totalmente de acuerdo en que una conversión implícita no representa una relación is-a y esta es la manera más formal de entender porque el código no compila…
    De todos modos yo también prefiero evitar las conversiones implícitas… 🙂

    Saludos!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *