2009-07-15 10 views
4

En tant que novice à la POO, j'essaie d'implémenter une méthode d'interface avec un paramètre de base en passant un paramètre de sous-classe (nécessaire). J'ai:Comment passer un paramètre de sous-classe lors de l'implémentation d'une interface en C#?


public interface IArticleDataAccess { int SaveArticle(NewsArticle thisArticle); } 

public AnalysisDataAccess : IArticleDataAccess { 
    public int SaveArticle(AnalysisArticle thisArticle) { 
    // Specific save code that needs properties of AnalysisArticle not found in NewsArticle. 
} 
public class AnalysisArticle : NewsArticle { 
    IArticleDataAccess dataAccess = new ArchivedArticleDataAccess(); 
    int Save() { 
    return dataAccess.SaveArticle(this); 
    } 
} 

L'erreur est « ArchivedArticleDataAccess' n'implémente pas membre d'interface 'IArticleDataAccess.SaveArticle (NewsArticle) » comme les types de paramètres ne sont pas les mêmes. Suis-je en train de faire une petite erreur ou de manquer un concept OOP fondamental? Y a-t-il un modèle que je peux utiliser pour faire ceci? Casting ou génériques? Ou est-ce une limitation de C# (pas de support de paramètres contravariant)?

Répondre

8

Vous ne pouvez pas appliquer la contravariance ici car cela casserait le contrat. Je pourrais envoyer toute sorte de NewsArticle à la classe savearticle via l'interface, puis votre AnalysisDataAccess serait barf ...

Ce que vous devez faire est de génériques d'usage, comme celui-ci:

public interface IArticleDataAccess<T> where T : NewsArticle 
{ 
    int SaveArticle(T thisArticle); 
} 

public AnalysisDataAccess : IArticleDataAccess<AnalysisArticle> { 
    public int SaveArticle(AnalysisArticle thisArticle) { 
    // Specific save code that needs properties of AnalysisArticle not found in NewsArticle. 
} 
public class AnalysisArticle : NewsArticle { 
    IArticleDataAccess<AnalysisArticle> dataAccess = new AnalysisArticleDataAccess(); 
    int Save() { 
    return dataAccess.SaveArticle(this); 
    } 
} 
0

Vous obtenez un compilation erreur car ArchivedArticleDataAccess doit mettre en œuvre IArticleDataAccess et sa déclaration doit ressembler à ceci:

public class ArchivedArticleDataAccess : IArticleDataAccess 
{ 
    public int SaveArticle(NewsArticle thisArticle) 
    { 
     // Save the article and return some integer 
    } 
} 

et non:

public class ArchivedArticleDataAccess : IArticleDataAccess 
{ 
    public int SaveArticle(AnalysisArticle thisArticle) 
    { 
     // Save the article and return some integer 
    } 
} 
2

Vous pourriez faire quelque chose comme ceci. Implémentez explicitement l'interface, assurez-vous que l'argument est le bon type et appelez la méthode publique qui gère le type que vous voulez réellement. Assurez-vous de savoir ce que vous voulez faire au cas où le nouvel article passé n'est pas un article d'analyse. En outre, réfléchissez à cette conception et assurez-vous que vous voulez et devez utiliser AnalysisDataAccess en tant que IArticleDataAccess.

IArticleDataAccess int SaveArticle(NewsArticle article) 
{ 
    AnalysisArticle analysisArticle = article as AnalysisArticle; 
    if (analysisArticle != null) 
      SaveArticle(analysisArticle); 
    //else handle error or another routine 
} 

public int SaveArticle(AnalysisArticle thisArticle) 
{ 
    //freely user analysis article members 
} 
0

Vous ne pouvez pas changer le type d'une méthode à mettre en œuvre sur une interface, vous devez changer de sorte que vous lancez le paramètre du type à l'intérieur de la mise en œuvre de la méthode comme ceci:

public AnalysisDataAccess : IArticleDataAccess { 
    public int SaveArticle(NewsArticle thisArticle) { 
    AnalysisArticle theArticle = thisArticle as AnalysisArticle; 
    if (theArticle != null) { 
    // Specific save code that needs properties of AnalysisArticle not found in 

NewsArticle. } }

Cela devrait fonctionner.

0

L'utilisation de génériques semble élégante. Mais en essayant de la mettre en œuvre, je ne l'utilise pas vraiment:


IArticleDataAccess<AnalysisArticle> dataAccess = new AnalysisArticleDataAccess(); 
//But instead have a DataAccess property in NewsArticle. Once defined as: 
IArticleDataAccess DataAccess { get; set; } 
//If I change to: 
IArticleDataAccess<NewsArticle> DataAccess { get; set; } 
// It can't be implicitly set to type IArticleDataAccess<AnalysisArticle>. 

Ai-je besoin aussi générique NewsArticle <T>? Le générique auto-référencé a l'air effrayant et nécessite beaucoup de changements. Il vaut mieux que chaque sous-classe réimplémente la propriété DataAccess avec le bon type? Ou est-ce que je manque quelque chose?

Questions connexes