首先,我正在使用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的类?
答案 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而不会破坏类型保证