Je sais que c’est vieux, mais je ne suis toujours pas très bon pour comprendre ces problèmes. Quelqu’un peut-il me dire pourquoi ce qui suit ne fonctionne pas (lève une exception d’ runtime
propos du casting)?
public abstract class EntityBase { } public class MyEntity : EntityBase { } public abstract class RepositoryBase where T : EntityBase { } public class MyEntityRepository : RepositoryBase { }
Et maintenant la ligne de casting:
MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever RepositoryBase baseRepo = (RepositoryBase)myEntityRepo;
Alors, quelqu’un peut-il expliquer en quoi cela est invalide? Et, vous n’êtes pas d’humeur à expliquer – y a-t-il une ligne de code que je peux utiliser pour faire ce casting?
RepositoryBase
n’est pas une classe de base de MyEntityRepository
. Vous recherchez une variance générique qui existe en C # dans une mesure limitée, mais ne s’appliquerait pas ici.
Supposons que votre classe RepositoryBase
ait une méthode comme celle-ci:
void Add(T entity) { ... }
Considérons maintenant:
MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever RepositoryBase baseRepo = (RepositoryBase )myEntityRepo; baseRepo.Add(new OtherEntity(...));
Maintenant, vous avez ajouté un type d’entité différent à un MyEntityRepository
… et cela ne peut pas être correct.
Fondamentalement, la variance générique n’est sans danger que dans certaines situations. En particulier, la covariance générique (que vous décrivez ici) n’est sûre que lorsque vous obtenez uniquement des valeurs “sortantes” de l’API; La contravariance générique (qui fonctionne à l’inverse) n’est sûre que si vous ne placez jamais de valeurs “dans” l’API (par exemple, une comparaison générale permettant de comparer deux formes par surface peut être considérée comme une comparaison de carrés).
En C # 4, cela est disponible pour les interfaces génériques et les delegates génériques, pas pour les classes – et uniquement avec les types de référence. Voir MSDN pour plus d’informations. Pour plus d’informations, consultez
Chaque fois que quelqu’un pose cette question, j’essaie de prendre son exemple et de le traduire en quelque chose utilisant des classes plus connues qui sont évidemment illégales (c’est ce que Jon Skeet a fait dans sa réponse ; mais je vais encore plus loin en effectuant cette traduction).
MyEntityRepository
par MySsortingngList
, comme ceci:
class MySsortingngList : List { }
Maintenant, vous semblez vouloir que le MyEntityRepository
puisse être converti en RepositoryBase
, le raisonnement étant que cela devrait être possible puisque MyEntity
dérive de EntityBase
.
Mais la ssortingng
dérive d’un object
, n’est-ce pas? Donc, par cette logique, nous devrions pouvoir MySsortingngList
une List
MySsortingngList
en une List
.
Voyons ce qui peut arriver si nous permettons cela …
var ssortingngs = new MySsortingngList(); ssortingngs.Add("Hello"); ssortingngs.Add("Goodbye"); var objects = (List
Uh-oh. Soudain, nous énumérons une List
et nous tombons sur un object Random
. Ce n’est pas bon.
Espérons que cela rend le problème un peu plus facile à comprendre.
Cela nécessite une covariance ou une contravariance, dont le support est limité en .Net, et ne peut pas être utilisé sur des classes abstraites. Vous pouvez cependant utiliser variance sur les interfaces. Par conséquent, une solution possible à votre problème consiste à créer un référentiel IR que vous utiliserez à la place de la classe abstraite.
public interface IRepository where T : EntityBase { //or "in" depending on the items. } public abstract class RepositoryBase : IRepository where T : EntityBase { } public class MyEntityRepository : RepositoryBase { } ... IRepository baseRepo = (IRepository )myEntityRepo;