(seguimiento a las observaciones de la respuesta aceptada.)
Sí, esto es una parte muy, muy confuso de la especificación. Todo el tema sobre "tipos abarcadoras" en particular es profundamente defectuoso. He intentado durante varios años encontrar el tiempo para reescribir completamente toda esa sección en algo más coherente, pero nunca ha sido una prioridad lo suficientemente alta.
Esencialmente, lo que tenemos aquí es una contradicción; nosotros decir que no hay conversiones implícitas definidas por el usuario que involucren interfaces, pero claramente eso no es cierto en este caso; hay una conversión implícita definida por el usuario de IC a Foo<IC>
, demostrado por el hecho de que una cadena va al Foo<IC>
a través de esa conversión.
Lo que realmente debería ser mejor haciendo hincapié en esta línea es que usted ha citado:
En particular, no es posible redefinir un explícita o implícita de conversión ya existente.
Eso es lo que motiva todo esto; el deseo de no permitirle pensar alguna vez que está realizando una prueba de tipo preservación de la representación cuando de hecho está llamando a un método definido por el usuario. Considere, por ejemplo, esta variación:
interface IBar {}
interface IFoo : IBar {}
class Foo<T> : IFoo
{
public static explicit operator Foo<T>(T input) { whatever }
}
class Blah : Foo<IBar> {}
...
IBar bar = new Blah();
Foo<IBar> foo = (Foo<IBar>)bar;
Ahora, ¿eso llaman la conversión explícita definida por el usuario o no? El objeto realmente se deriva de Foo, por lo que esperaría que no lo haga; esto debería ser una prueba de tipo simple y una asignación de referencia, no una llamada a un método de ayuda. Un molde en un valor de interfaz siempre se trata como una prueba de tipo porque casi siempre es posible que el objeto realmente sea de ese tipo y realmente implementa esa interfaz. No queremos negarle la posibilidad de realizar una conversión de preservación de representación barata.
Wow. Qué extraño requisito. Me gustaría saber de Lippert, Skeet u otro experto en C# por qué los tipos de interfaz no funcionarán para esto; seguramente debe haber una buena razón detrás de esta rareza. –
Después de experimentar un poco, veo que las interfaces parecen ser la clave aquí. Lo que es extraño es que parece menos un salto mental para el compilador averiguar qué hacer muchas veces. Hmm. –
Estaría muy interesado en una explicación de cómo compila la primera muestra. Dadas las reglas en la sección 6.4.4, no veo cómo se elige la conversión 'Foo' dado que 'ICloneable' no abarca' string' (ya que 'ICloneable' es una interfaz) y' Foo 'es no comprendido por 'Foo ' (ya que no hay una conversión implícita de 'Foo ' a 'Foo '). Quizás 6.1.9 "Conversiones implícitas que involucran parámetros de tipo" está entrando en juego de alguna manera? –
zinglon