2016-11-07 84 views
0

我很難說出我的標題,所以現在讓我試着闡述一下。首先這裏是我的相關代碼:如何在包含子類的超類列表上調用子類方法

class Question 
    { 
     static bool checkFile(XElement q) 
     { 
      foreach (XElement a in q.Descendants()) 
      { 
       if (a.Name.LocalName == "file") 
       { 
        return true; 
       } 
      } 
      return false; 
     } 
     protected string questionText; 
     protected List<File> files; 
     protected Question question; 
     public Question(XElement q) 
     { 
      questionText = q.Element("questiontext").Element("text").Value.ToString(); 
      string name = q.Attribute("type").Value.ToString(); 
      if (checkFile(q)) 
       files.Add(new File(q)); 
     } 
    } 
    class multichoice : Question 
    { 
     private List<string> answers; 
     public multichoice(XElement q) 
      : base(q) 
     { 
      foreach (XElement a in q.Elements()) 
      { 
       if (a.Name.LocalName == "answer") 
        answers.Add(a.Element("text").Value.ToString()); 
      } 
     } 
     public void writeQuestion(HtmlTextWriter writer) 
     { 
      writer.RenderBeginTag("p"); 
      writer.Write("<strong>Multiple Choice: </strong>" + this.questionText); 
      writer.RenderEndTag(); 
      writer.AddAttribute("type", "A"); 
      writer.RenderBeginTag("ol"); 
      foreach (string answer in answers) 
      { 
       writer.RenderBeginTag("li"); 
       writer.Write(answer); 
       writer.RenderEndTag(); 
      } 
      writer.RenderEndTag(); 
     } 
    } 
    class truefalse : Question 
    { 
     public truefalse(XElement q) 
      : base(q) 
     { 

     } 
     public void writeQuestion(HtmlTextWriter writer) 
     { 
      writer.RenderBeginTag("strong"); 
      writer.Write("True or False : "); 
      writer.RenderEndTag(); 
      writer.Write(questionText); 
     } 
    } 

所以我創建了多種類型的問題,都是「問題」的子類。問題包含適用於每種類型問題的所有數據,這些子類包含它們獨有的方法,主要是「writeQuestion」。 現在我試圖用它做的是這樣的:

static List<Question> collectQuestions(XDocument doc) 
    { 
     XDocument xdoc = doc; 
     string elementName = null; 
     List<Question> questions = null; 
     foreach (XElement q in xdoc.Descendants("question")) 
     { 
      elementName = q.Attribute("type").Value.ToString(); 
      if (elementName != "category") 
       continue; 
      if (elementName == "truefalse") 
       questions.Add(new truefalse(q)); //writeTrueFalse(writer, q); 
      else if (elementName == "calculatedmulti") 
       questions.Add(new calculatedmulti(q)); // writeCalculatedMulti(writer, q); 
      else if (elementName == "calculatedsimple") 
       questions.Add(new calculatedsimple(q)); // writeCalculatedSimple(writer, q); 
      else if (elementName == "ddwtos") 
       questions.Add(new Draganddrop(q)); //writeDragAndDrop(writer, q); 
      else if (elementName == "description") 
       questions.Add(new Description(q)); // writeDescription(writer, q); 
      else if (elementName == "essay") 
       questions.Add(new Essay(q)); // writeEssay(writer, q); 
      else if (elementName == "gapselect") 
       questions.Add(new Gapselect(q)); // writeGapSelect(writer, q); 
      else if (elementName == "matching") 
       questions.Add(new Matching(q)); // writeMatching(writer, q); 
      else if (elementName == "multichoice") 
       questions.Add(new multichoice(q)); // writeMultipleChoice(writer, q); 
      else if (elementName == "multichoiceset") 
       questions.Add(new Allornothing(q)); // writeAllOrNothing(writer, q); 
      else if (elementName == "numerical") 
       questions.Add(new Numerical(q)); // writeNumerical(writer, q); 
      else if (elementName == "shortanswer") 
       questions.Add(new shortanswer(q)); // writeShortAnswer(writer, q); 
      else 
       continue; 
     } 
     return questions; 
    } 
questions = collectQuestions(someDocument); 
foreach (Question question in questions) 
      { 
       question.writeQuestion(writer); 
      } 

有沒有辦法來調用每個項目的writeQuestion?現在它當然會給出這樣的錯誤:即使它的每個子類都存在,Questions也不包含writeQuestion的定義。請評論,如果我應該補充更多的澄清,我的代碼已經有點糾結,因爲我一再重複它。對於像這樣的課程,我很體面,所以我可能會錯過一些關鍵概念,請指出您看到的任何內容,謝謝。

+0

爲什麼在Question超類中沒有'writeQuestion'方法?添加一個或爲此創建一個接口。 – RBarryYoung

+0

順便說一句,即使Eric Lippert正確地解決了你的問題,我也看到了一個不明確的問題分離:你將類和它們如何表示爲HTML。這是一個非常糟糕的主意。 –

+0

@Matias Fidemraizer,我的完整代碼正在做的是解析一個xml文件並將其重新格式化爲html文件中所需的格式。我在這些問題中存儲我需要的數據以及代表每個問題的代碼塊。然後我輸出這些塊到文件。到目前爲止,它運作良好。這是什麼讓這是一個壞主意,你會建議什麼? – Zannith

回答

4

使基類abstract,向基類添加一個摘要WriteQuestion成員,然後在每個具體實現中添加override它。

+0

@Eric Lippert,謝謝你的幫助,我將超類標記爲抽象,並在超類中創建了一個抽象的writequestion方法,現在當我創建一個覆寫writequestion它說的子類沒有合適的方法被發現覆蓋。你可能會添加一個如何在我的代碼中工作的例子嗎? – Zannith

+0

您是否使該方法重寫虛擬? – Botonomous

+0

好的,我得到了那部分工作,謝謝你的幫助!關於修復這個問題的錯誤當然;) – Zannith

1

恕我直言,我會將你的代碼分成更多的類,以獲得良好的問題分離。

您的Question類和專業化(即派生類)不應該知道它們的存儲方式,也不應該知道它們是如何轉換成HTML表示形式的。

我將定義一個名爲XmlQuestionConverter類:

public class XmlQuestionConverter 
{ 
    public XmlQuestionConverter() 
    { 
     TypeToConvertMap = new Dictionary<Type, Action<Question, XElement>> 
     { 
      { typeof(TrueFalseQuestion), new Action<Question, XElement>(ConvertTrueFalseFromXml) } 
      // other mappings... 
     }; 
    } 
    private Dictionary<Type, Action<Question, HtmlTextWriter>> TypeToConvertMap 
    { 
     get; 
    } 

    // This dictionary maps element names to their type 
    private Dictionary<string, Type> QuestionTypeMap { get; } = new Dictionary<string, Type>() 
    { 
      { "truefalse", typeof(TrueFalseQuestion) }, 
      { "multichoice", typeof(MultiChoiceQuestion) } 
      // And so on 
    }; 

    public IList<Question> ConvertFromXml(XDocument questionsDocument) 
    { 
     // This will get all question elements and it'll project them 
     // into concrete Question instances upcasted to Question base 
     // class 
     List<Question> questions = questionsDocument 
        .Descendants("question") 
        .Select 
        (
         element => 
         { 
          Type questionType = QuestionTypeMap[q.Attribute("type").Value]; 
          Question question = (Question)Activator.CreateInstance(questionType); 

          // Calls the appropiate delegate to perform specific 
          // actions against the instantiated question 
          TypeToConvertMap[questionType](question, element); 

          return question; 
         } 
        ).ToList(); 

     return questions; 
    } 

    private void ConvertTrueFalseFromXml(TrueFalseQuestion question, XElement element) 
    { 
      // Here you can populate specific attributes from the XElement 
      // to the whole typed question instance! 
    } 
} 

現在你可以一個XDocument轉換爲一個問題列表,我們準備把它們變成HTML與HtmlTextWriter

public class HtmlQuestionConverter 
{ 
    public HtmlQuestionConverter() 
    { 
     TypeToConvertMap = new Dictionary<Type, Action<Question, HtmlTextWriter>> 
     { 
      { typeof(TrueFalseQuestion), new Action<Question, HtmlTextWriter>(ConvertTrueFalseToHtml) } 
      // other mappings... 
     }; 
    } 

    private Dictionary<Type, Action<Question, HtmlTextWriter>> TypeToConvertMap 
    { 
     get; 
    } 

    public void ConvertToHtml(IEnumerable<Question> questions, HtmlTextWriter htmlWriter) 
    { 
     foreach (Question question in questions) 
     { 
      // Calls the appropiate method to turn the question 
      // into HTML using found delegate! 
      TypeToConvertMap[question.GetType()](question, htmlWriter); 
     } 
    } 

    private void ConvertTrueFalseToHtml(Question question, HtmlTextWriter htmlWriter) 
    { 
     // Code to write the question to the HtmlTextWriter... 
    } 
} 

按照這種方式,我沒有看到你需要多態性:

XmlQuestionConverter xmlQuestionConverter = new XmlQuestionConverter(); 
IList<Question> questions = xmlQuestionConverter.ConvertFromXml(xdoc); 

HtmlQuestionConverter htmlQuestionConverter = new HtmlQuestionConverter(); 
htmlQuestionConverter.ConvertToHtml(questions, htmlWriter); 

注意:我無法嘗試執行此代碼,但我不能100%確定它是否可行,但瞭解如何以清晰的關注點分離來實現代碼是一個良好的開始!您可能需要做一些調整,以使我的代碼適應您的實際使用情況。

+0

哇,我真的很感謝你付出的努力。它絕對看起來比我有的更好。當我解決了我在程序中遇到的所有其他問題後,我會看到如何將我的模板轉換爲與此類似的格式。我再次欣賞教學。 – Zannith

+0

@詹尼斯不客氣!明天我會調整甚至定義像'TrueFalseQuestionConverter'這樣的元素轉換器,而不是使用委託。 –

+0

@贊尼斯BTW我有一些想法...你真的需要使用XML嗎? JSON序列化非常容易與JSON.NET一起使用。您可以使用JSON而非XML極其簡化您的代碼。文本問題的對象會自動完成,你會扔掉'XmlQuestionConverter'。 –

相關問題