Estou tentando escrever algoritmos genéricos em c # que podem funcionar com entidades geométricas de diferentes dimensões.
No exemplo a seguir, eu tenho Point2
e Point3
, ambos implementando uma IPoint
interface simples .
Agora eu tenho uma função GenericAlgorithm
que chama uma função GetDim
. Existem várias definições dessa função com base no tipo. Há também uma função de fallback definida para qualquer coisa que implemente IPoint
.
Inicialmente, eu esperava que a saída do programa a seguir fosse 2, 3. No entanto, é 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 algum motivo as informações do tipo concreto são perdidas GenericAlgorithm
. Não entendo completamente por que isso acontece, mas tudo bem. Se não posso fazer dessa maneira, que outras alternativas tenho?
NumDims
propriedade esteja disponível. Por que você está ignorando isso em alguns casos?GetDim
(ou seja, eu passo uma,Point4
masGetDim<Point4>
não existe). No entanto, não parece que o compilador se preocupe em procurar uma implementação especializada.Respostas:
Este método:
... vai sempre chamar
GetDim<T>(T point)
. A resolução de sobrecarga é executada em tempo de compilação e, nesse estágio, não há outro método aplicável.Para que a resolução de sobrecarga seja chamada no tempo de execução , você precisará usar a digitação dinâmica, por exemplo
Mas geralmente é uma idéia melhor usar herança para isso - no seu exemplo, obviamente, você pode ter apenas o método único e retornar
point.NumDims
. Presumo que no seu código real há alguma razão para o equivalente ser mais difícil de fazer, mas sem mais contexto, não podemos aconselhar sobre como usar a herança para executar a especialização. Essas são as suas opções:fonte
AxisAlignedBoundingBox2
eAxisAlignedBoundingBox3
. Eu tenho umContains
método estático que é usado para determinar se uma coleção de caixas contém umLine2
ouLine3
(qual deles depende do tipo das caixas). A lógica do algoritmo entre os dois tipos é exatamente a mesma, exceto que o número de dimensões é diferente. Também existem chamadas paraIntersect
internamente que precisam ser especializadas no tipo correto. Quero evitar chamadas de funções virtuais / dinâmicas, e é por isso que estou usando genéricos ... é claro, posso apenas copiar / colar o código e seguir em frente.A partir do C # 8.0, você deve fornecer uma implementação padrão para sua interface, em vez de exigir o método genérico.
A implementação de um método genérico e sobrecargas por
IPoint
implementação também viola o Princípio de Substituição de Liskov (o L no SOLID). Seria melhor inserir o algoritmo em cadaIPoint
implementação, o que significa que você só precisa de uma única chamada de método:fonte
Padrão do visitante
como alternativa ao
dynamic
uso, convém usar um padrão de visitante como abaixo:fonte
Por que você não define a função GetDim na classe e na interface? Na verdade, você não precisa definir a função GetDim, basta usar a propriedade NumDims.
fonte