如何强制子类具有超类属性的特定子类?

时间:2016-10-13 17:43:37

标签: c# entity-framework inheritance architecture abstract-class

首先,我正在使用EF,因为我正在使用C#构建MVC应用程序。我希望不同类型的考试有不同类型的问题。这是我的抽象类:

public abstract class Exam
{
    public int Id { get; set; }
    public string Description { set; get; }

    public abstract ICollection<Question> GetQuestions();
    public abstract void SetQuestions(ICollection<Question> questions);
}

public abstract class Question
{
    public int Id { get; set; }
    public string Description { set; get; }

    public abstract Exam getExam();
    public abstract void setExam(Exam exam);
}

请注意,我没有在Exam类声明中使用典型的public virtual ICollection<Question>,而是创建了一个抽象的setter和getter。问题类中的Exam属性也是如此。

以下是我的具体考试课程:

[Table("SingleExam")]
public class SingleExam : Exam
{
    public virtual ICollection<SingleQuestion> Questions { get; set; }

    public override ICollection<Question> GetQuestions() { return Questions as ICollection<Question>; }
    public override void SetQuestions(ICollection<Question> questions)
    {
        if (!(questions is ICollection<SingleQuestion>))
            throw new ArgumentException("You must set single questions.");

        Questions = questions as ICollection<SingleQuestion>;
    }
}

[Table("MultipleExam")]
public class MultipleExam : Exam
{
    public virtual ICollection<MultipleQuestion> Questions { get; set; }

    public override ICollection<Question> GetQuestions() { return Questions as ICollection<Question>; }
    public override void SetQuestions(ICollection<Question> questions)
    {
        if (!(questions is ICollection<MultipleQuestion>))
            throw new ArgumentException("You must set multiple questions.");

        Questions = questions as ICollection<MultipleQuestion>;
    }
}

......我的具体问题类:

[Table("SingleQuestion")]
public class SingleQuestion : Question
{
    public int ExamId { get; set; }
    public virtual SingleExam Exam { get; set; }

    public override Exam getExam() { return Exam; }
    public override void setExam(Exam exam)
    {
        if (!(exam is SingleExam))
            throw new ArgumentException("You must set a SingleExam");

        Exam = exam as SingleExam;
    }
}

[Table("MultipleQuestion")]
public class MultipleQuestion : Question
{
    public int ExamId { get; set; }
    public virtual MultipleExam Exam { get; set; }

    public override Exam getExam() { return Exam; }
    public override void setExam(Exam exam)
    {
        if (!(exam is MultipleExam))
            throw new ArgumentException("You must set a MultipleExam");

        Exam = exam as MultipleExam;
    }
}

我做了这一切是因为MultipleExam应该只有MultipleQuestions,SingleExam应该只有SingleQuestions,就像MultipleQuestion应该有MultipleExam和Single问题应该有SingleExam一样。

是否有更好的方法来确保类'A'的子类包含或具有类'B'的特定子类(与我的考试和问题一样),并通过摘要访问它没有抽象的getter和setter的类?

1 个答案:

答案 0 :(得分:1)

正如其他人所提到的,我认为你的问题让你复杂化了。 然而;你的问题是类型保证,我会尽力回答。

首先是代码:

public interface IExam<out T> where T:IQuestion {
  int Id { get; set; }
  string Description { set; get; }
  IEnumerable<T> GetQuestions();
}

public interface IQuestion{
  int Id { get; set; }
  string Description { set; get; }
  IExam<IQuestion> Exam { get; }
}

public class SingleQuestion:IQuestion {
  public string Description { get; set; }
  public int Id { get; set; }
  IExam<IQuestion> IQuestion.Exam {
    get { return Exam; }
  }
  public SingleExam Exam { get; set; }
}

public class SingleExam:IExam<SingleQuestion> {
  public int Id { get; set; }
  public string Description { get; set; }

  private IEnumerable<SingleQuestion> _questions;
  public IEnumerable<SingleQuestion> GetQuestions() {
    return _questions;
  }

  public void SetQuestions(IEnumerable<SingleQuestion> questions) {
    _questions = questions;
  }
}

首先,我们用接口替换了抽象类。 这是必需的,因为我们希望在IQuestion上使IExam协变,并且协方差只能在接口中定义。这也是我们为集合更改为IEnumerable的原因。

注意我们没有在IExam中定义SetQuestions方法,这是因为我们不能。长期以来,这是因为这会使T逆变而且逆变,从而导致无法进行类型保证的情况。

IQuestions相当直接,没有真正的变化。我想,你可以把它留作抽象类型。

现在实施: 在SingleQuestion中,我们必须明确地实现Exam,期望IExam然后使用返回SingleExam的属性来遮蔽它。 这使我们能够返回最精确的考试类型。

SingleQuestion sq = new SingleQuestion();
IQuestion q = sq; //Upcast
sq.Exam; //returns a SingleExam
q.Exam; //returns a IExam<IQuestion>

在SingleExam中,您现在可以设置问题并对其进行限制,以便只添加SingleQuestions。

另外,现在更容易理解为什么无法在IExam中定义SetQuestions。请考虑以下事项:

SingleExam se = new SingleExam();
IExam<IQuestion> singleUpcast = se;
//What type of question can we set on singleUpcast?

我们所知道的是singleUpcast包含IQuestions但我们不能只添加IQuestions,因为singleUpcast最终是SingleExam的一个实例,它承诺只能设置SingleQuestions。简而言之,不可能知道哪些类型可以添加到IExam而不会破坏类型保证