J'essaie d'écrire des algorithmes génériques en C # qui peuvent fonctionner avec des entités géométriques de différentes dimensions.
Dans l'exemple artificiel suivant, j'ai Point2
et Point3
, tous deux implémentant une IPoint
interface simple .
Maintenant, j'ai une fonction GenericAlgorithm
qui appelle une fonction GetDim
. Il existe plusieurs définitions de cette fonction en fonction du type. Il existe également une fonction de secours qui est définie pour tout ce qui est implémenté IPoint
.
Au départ, je m'attendais à ce que la sortie du programme suivant soit 2, 3. Cependant, c'est 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, donc pour une raison quelconque, les informations de type concret sont perdues GenericAlgorithm
. Je ne comprends pas vraiment pourquoi cela se produit, mais très bien. Si je ne peux pas le faire de cette façon, quelles autres alternatives ai-je?
NumDims
disponibilité de la propriété. Pourquoi l'ignorez-vous dans certains cas?GetDim
(c'est-à-dire que je passe unPoint4
maisGetDim<Point4>
n'existe pas). Cependant, il ne semble pas que le compilateur se soucie de rechercher une implémentation spécialisée.Réponses:
Cette méthode:
... sera toujours appeler
GetDim<T>(T point)
. La résolution de surcharge est effectuée au moment de la compilation , et à ce stade, il n'y a pas d'autre méthode applicable.Si vous souhaitez que la résolution de surcharge soit appelée au moment de l'exécution , vous devez utiliser le typage dynamique, par exemple
Mais c'est généralement une meilleure idée d'utiliser l'héritage pour cela - dans votre exemple, vous pouvez évidemment avoir une seule méthode et retourner
point.NumDims
. Je suppose que dans votre vrai code, il y a une raison pour laquelle l'équivalent est plus difficile à faire, mais sans plus de contexte, nous ne pouvons pas vous conseiller sur la façon d'utiliser l'héritage pour effectuer la spécialisation. Ce sont vos options cependant:la source
AxisAlignedBoundingBox2
etAxisAlignedBoundingBox3
. J'ai uneContains
méthode statique qui est utilisée pour déterminer si une collection de boîtes contient unLine2
ouLine3
(lequel dépend du type de boîtes). La logique de l'algorithme entre les deux types est exactement la même, sauf que le nombre de dimensions est différent. Il existe également des appels à l'Intersect
interne qui doivent être spécialisés dans le bon type. Je veux éviter les appels de fonction virtuels / dynamiques, c'est pourquoi j'utilise des génériques ... bien sûr, je peux simplement copier / coller le code et continuer.À partir de C # 8.0, vous devriez être en mesure de fournir une implémentation par défaut pour votre interface, plutôt que d'exiger la méthode générique.
L'implémentation d'une méthode générique et les surcharges par
IPoint
implémentation violent également le principe de substitution de Liskov (le L dans SOLID). Il serait préférable de pousser l' algorithme dans chaqueIPoint
implémentation, ce qui signifie que vous ne devriez avoir besoin que d'un seul appel de méthode:la source
Modèle de visiteur
comme alternative à l'
dynamic
utilisation, vous pouvez utiliser un modèle de visiteur comme ci-dessous:la source
Pourquoi ne définissez-vous pas la fonction GetDim dans la classe et l'interface? En fait, vous n'avez pas besoin de définir la fonction GetDim, utilisez simplement la propriété NumDims.
la source