Estoy tratando de escribir algoritmos genéricos en C # que pueden funcionar con entidades geométricas de diferentes dimensiones.
En el siguiente ejemplo artificial que tengo Point2
y Point3
, ambos implementamos una IPoint
interfaz simple .
Ahora tengo una función GenericAlgorithm
que llama a una función GetDim
. Existen múltiples definiciones de esta función según el tipo. También hay una función alternativa que se define para cualquier cosa que implemente IPoint
.
Inicialmente esperaba que la salida del siguiente programa fuera 2, 3. Sin embargo, es 0, 0.
interface IPoint {
public int NumDims { get; }
}
public struct Point2 : IPoint {
public int NumDims => 2;
}
public struct Point3 : IPoint {
public int NumDims => 3;
}
class Program
{
static int GetDim<T>(T point) where T: IPoint => 0;
static int GetDim(Point2 point) => point.NumDims;
static int GetDim(Point3 point) => point.NumDims;
static int GenericAlgorithm<T>(T point) where T : IPoint => GetDim(point);
static void Main(string[] args)
{
Point2 p2;
Point3 p3;
int d1 = GenericAlgorithm(p2);
int d2 = GenericAlgorithm(p3);
Console.WriteLine("{0:d}", d1); // returns 0 !!
Console.WriteLine("{0:d}", d2); // returns 0 !!
}
}
OK, por alguna razón, la información del tipo concreto se pierde GenericAlgorithm
. No entiendo completamente por qué sucede esto, pero está bien. Si no puedo hacerlo de esta manera, ¿qué otras alternativas tengo?
NumDims
propiedad esté disponible. ¿Por qué lo ignoras en algunos casos?GetDim
(es decir, paso unPoint4
peroGetDim<Point4>
no existe). Sin embargo, no parece que el compilador se moleste en buscar una implementación especializada.Respuestas:
Este método:
... siempre llamará
GetDim<T>(T point)
. La resolución de sobrecarga se realiza en tiempo de compilación , y en esa etapa no hay otro método aplicable.Si desea que se invoque la resolución de sobrecarga en el momento de la ejecución , deberá usar la tipificación dinámica, p. Ej.
Pero generalmente es una mejor idea usar la herencia para esto; en su ejemplo, obviamente podría tener el método único y regresar
point.NumDims
. Supongo que en su código real hay alguna razón por la que es más difícil hacer el equivalente, pero sin más contexto no podemos aconsejar sobre cómo usar la herencia para realizar la especialización. Sin embargo, esas son sus opciones:fuente
AxisAlignedBoundingBox2
yAxisAlignedBoundingBox3
. Tengo unContains
método estático que se utiliza para determinar si una colección de cuadros contiene unLine2
oLine3
(cuál depende del tipo de cuadros). La lógica del algoritmo entre los dos tipos es exactamente la misma, excepto que el número de dimensiones es diferente. También hay llamadasIntersect
internas que necesitan especializarse para el tipo correcto. Quiero evitar las llamadas a funciones virtuales / dinámicas, por eso estoy usando genéricos ... por supuesto, puedo copiar / pegar el código y seguir adelante.A partir de C # 8.0, debería poder proporcionar una implementación predeterminada para su interfaz, en lugar de requerir el método genérico.
Implementar un método genérico y sobrecargas por
IPoint
implementación también viola el Principio de sustitución de Liskov (la L en SÓLIDO). Sería mejor insertar el algoritmo en cadaIPoint
implementación, lo que significa que solo debería necesitar una sola llamada al método:fuente
Patrón de visitante
Como alternativa al
dynamic
uso, es posible que desee utilizar un patrón de visitante como se muestra a continuación:fuente
¿Por qué no define la función GetDim en clase e interfaz? En realidad, no necesita definir la función GetDim, solo use la propiedad NumDims.
fuente