Как передать параметр подкласса при реализации интерфейса в C #? - PullRequest
4 голосов
/ 15 июля 2009

Как новичок в ООП, я пытаюсь реализовать метод интерфейса с базовым параметром, передав (необходимый) параметр подкласса. У меня есть:


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);
  }
}

Ошибка «ArchivedArticleDataAccess» не реализует элемент интерфейса «IArticleDataAccess.SaveArticle (NewsArticle)» », поскольку типы параметров не совпадают.

Я делаю небольшую ошибку или пропускаю основную концепцию ООП? Есть ли шаблон, который я могу использовать, чтобы сделать это? Кастинг или дженерики? Или это ограничение C # (без поддержки контравариантных параметров)?

Ответы [ 5 ]

8 голосов
/ 15 июля 2009

Здесь нельзя применять контравариантность, поскольку это нарушит контракт. Я мог бы отправить любой тип NewsArticle в класс SaveArticle через интерфейс, и тогда ваш AnalysisDataAccess будет прервать ...

Что вы должны сделать, это использовать дженерики, как это:

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);
  }
}
2 голосов
/ 15 июля 2009

Вы могли бы сделать что-то вроде этого. Явно реализуйте интерфейс, убедитесь, что аргумент является правильным типом, и вызовите открытый метод, который обрабатывает тип, который вы на самом деле хотите. Просто убедитесь, что вы знаете, что вы хотите сделать, если переданная новая статья не является аналитической статьей. Кроме того, продумайте этот дизайн и убедитесь, что вы хотите и должны использовать AnalysisDataAccess в качестве 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 голосов
/ 16 июля 2009

Использование дженериков выглядит элегантно. Но, пытаясь реализовать это, я фактически не использую:


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>.

Нужно ли также универсальный для NewsArticle ? Самообращающийся общий вид выглядит страшно и требует много изменений. Лучше, чтобы каждый подкласс переопределял свойство DataAccess нужного типа? Или я что-то упустил?

0 голосов
/ 15 июля 2009

Вы не можете изменить тип метода, который вы реализуете на интерфейсе, вам нужно изменить его, чтобы вы привели параметр к нужному типу внутри реализации метода, как показано ниже:

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. } }

Это должно работать.

0 голосов
/ 15 июля 2009

Вы получаете ошибку времени компиляции, потому что ArchivedArticleDataAccess должен реализовывать IArticleDataAccess, и его объявление должно выглядеть следующим образом:

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

а не:

public class ArchivedArticleDataAccess : IArticleDataAccess 
{
    public int SaveArticle(AnalysisArticle thisArticle) 
    {
        // Save the article and return some integer
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...