2012-03-07 4 views
3

J'ai une application structurée comme une couche de service qui utilise une couche de référentiel pour la persistance. J'essaye de créer une classe de contrôleur générique pour réutiliser le comportement partagé mais j'ai du mal à essayer de définir les paramètres génériques. Le code suivant:Conversion implicite en classes génériques C#

public class BusinessEntity 
{ } 

public class Person : BusinessEntity 
{ } 

public interface IRepository<T> where T : BusinessEntity 
{ } 

public interface IService<T, R> 
    where T : BusinessEntity 
    where R : IRepository<T> 
{ } 

public partial interface IPersonRepository : IRepository<Person> 
{ } 

public interface IPersonService : IService<Person, IPersonRepository> 
{ } 

public abstract class BaseController<X, Y> 
    where X : BusinessEntity 
    where Y : IService<X, IRepository<X>> 
{ } 

public class PersonController : BaseController<Person, IPersonService> 
{ } 

échoue à la compilation avec

Le type ConsoleApplication.IPersonService ne peut pas être utilisé comme paramètre de type Y dans le type générique ou méthode ConsoleApplication.BaseController<X,Y>. Il n'y a pas de conversion de référence implicite ConsoleApplication.IPersonService-ConsoleApplication.IService<ConsoleApplication.Person,ConsoleApplication.IRepository<ConsoleApplication.Person>>

cela fonctionne

public interface IPersonService : IService<Person, IRepository<Person>> 

mais je perdre le référentiel personnalisé

Il y a un moyen de rendre le compilateur est un IPersonRepository réalisons des IRepository<Person>?

+1

droit est ce parce que 'IRepository où X: Person' n'est pas la même chose que 'IRepository ' par défaut, aucune variance pour ces types génériques par défaut. Quelle version de .NET Framework utilisez-vous? – sll

+0

Si j'avais la capacité, je vous décernerais un badge "a cassé le compilateur". De toutes les 5 minutes que j'ai passées à essayer de le faire fonctionner, ma conclusion est que le compilateur n'est pas assez intelligent pour faire ce que vous voulez qu'il fasse. – PlayDeezGames

+0

J'utilise .Net 4. Je n'ai pas pensé à la variance, je pense que ça mérite un essai. –

Répondre

4
public class BusinessEntity 
{ } 

public class Person : BusinessEntity 
{ } 

public interface IRepository<T> where T : BusinessEntity 
{ } 

public interface IService<T, R> 
    where T : BusinessEntity 
    where R : IRepository<T> 
{ } 

public partial interface IPersonRepository : IRepository<Person> 
{ } 

public interface IPersonService : IService<Person, IPersonRepository> 
{ } 

public abstract class BaseController<X, Y, Z> 
    where X : BusinessEntity 
    where Y : IService<X, Z> 
    where Z : IRepository<X> 
{ } 

public class PersonController : BaseController<Person, IPersonService, IPersonRepository> 
{ } 

Pour répondre à votre commentaire:

IPersonService peut étendre la classe de service de base pour ajouter des installations personnalisées, comme FindPersonsUnderAge(). Pour cela, il nécessite un référentiel personnalisé. En fait LINQ évite beaucoup de code de dépôt personnalisé, mais parfois ils sont requis.

IPersonService ne pouvait pas faire cela sans que le type de référentiel soit un paramètre de type? Par exemple:

public interface IService<T> where T : BusinessEntity { } 

public interface IPersonService : IService<Person> 
{ 
    IEnumerable<Person> FindPersonsByAge(double minAge, double maxAge); 
} 

public class Service<T, R> : IService<T> 
    where T : BusinessEntity 
    where R : IRepository<T> 
{ } 

public class PersonService : Service<Person, IPersonRepository>, IPersonService 
{ } 
+0

Je n'aime pas complètement le fait que le développeur de l'interface utilisateur connaisse les dépôts, mais c'est un compromis acceptable. –

+0

@JamesGordon Dans ce cas, je tournerais pour 'interface IPersonService: IService ', donc le consommateur du service n'a pas besoin de savoir quoi que ce soit sur la façon dont le service persiste ses données - même s'il repose sur un 'IRepository < > '. Quelles méthodes de PersonService nécessitent un paramètre de type pour le référentiel de personne? – phoog

+0

IPersonService peut étendre la classe de service de base pour ajouter des fonctionnalités personnalisées, telles que FindPersonsUnderAge(). Pour cela, il nécessite un référentiel personnalisé. En fait LINQ évite beaucoup de code de dépôt personnalisé, mais parfois ils sont requis. –

0

Merci à SLL pour moi pointant dans la bonne direction

public interface IService<T, out R> 
    where T : BusinessEntity 
    where R : IRepository<T> 
{ } 

fait le tour