Inferencia de tipos sobre IEnumerable<T>
-
11-09-2019 - |
Pregunta
Ya hay algunas publicaciones en el desbordamiento de la pila sobre este tipo de cosas, pero no son exactamente iguales, así que me disculpo de antemano si esto es algo que ya ha sido respondido.
Por qué esto no funciona:
public class MyBase { }
public class MyUtils
{
public bool Foo<T> (T myObject) { return true; }
public bool Foo (MyBase myBaseObject) { return false; }
public void Go<T> (IEnumerable<T> items)
{
foreach (var item in items)
{
// this test fails
Assert.IsFalse (Foo (item));
}
}
}
Si llamo a Go() arriba y paso una carga de objetos MyBase, cada llamada a Foo llamará al Foo () genérico, que devuelve verdadero.
new MyUtils ().Go (new MyBase[] { new MyBase (), new MyBase () });
¿Por qué no llama a la versión especializada MyBase?Si llamo a Foo (nuevo MyBase ()) directamente, infiere correctamente qué llamada realizar.¿Esto se debe a la falta de covarianza para las colecciones en C#3, o simplemente estoy siendo tonto y no lo hago correctamente?
¡Gracias!
isaac
Solución
No llama al "especializado" porque el compilador elige qué método llamar cuando el programa (que en este caso es el Go
función) es compilado, no cuando se ejecuta.
Cuando el compilador está compilando el Go
función, la única información que tiene es que hay algún objeto de tipo T
.No tiene idea de que en algún momento posterior le puedas proporcionar un objeto del tipo MyBase
.La única opción que tiene es elegir el Foo<T>
sobrecarga, por lo que lo integra en el programa compilado.
Si desea que una aplicación elija sobrecargas en tiempo de ejecución y elija la mejor sobrecarga mirando el objeto mientras la aplicación se está ejecutando, eso se llama "despacho dinámico" y solo lo usan lenguajes dinámicos como Ruby, Python, PHP. , etc.
C#3 es completamente estático y no admite esto.Tendrías que escribir una declaración if en tu código para verificar el tipo si quisieras que funcione de esta manera.C#4, por otro lado, tiene cierto soporte dinámico.Si estuviera escribiendo este código en C# 4, podría declarar la función 'Ir' de la siguiente manera:
public void Go<T> (IEnumerable<dynamic> items)
Luego usaría el envío dinámico en tiempo de ejecución para elegir qué sobrecarga se llama y llamaría a la sobrecarga especializada para tomar MyBase
Otros consejos
Según C especificaciones # (7.4.3.2), el método no genérico es mejor que el genérico, por lo que debe ser seleccionado.
Sin embargo, creo que el genérico es recogido en su caso, ya que se llama dentro de un contexto invocación genérica (el "foreach" loop).