2016-11-07 7 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 по каждому из пунктов? прямо сейчас он, конечно же, дает ошибку, что вопросы не содержат определения для writeQuestion, даже если каждый из его подклассов. Прошу прокомментировать, если я должен добавить что-то еще для разъяснений, мой код немного искалечен, поскольку я неоднократно его перерабатывал. Я прилично новичок в работе с такими классами, поэтому мне могут не хватать некоторых ключевых концепций, пожалуйста, укажите все, что вы видите, благодарю вас.

+0

Почему в вопросительном суперклассе нет метода 'writeQuestion'? Либо добавьте его, либо создайте для этого интерфейс. – RBarryYoung

+0

Кстати, когда Эрик Липперт прав насчет того, как решить вашу проблему, я вижу непонятное разделение проблем: вы связываете свои классы с тем, как они представлены как HTML. Это очень плохая идея. –

+0

@Matias Fidemraizer, то, что делает мой полный код, - это разбор XML-файла и переформатирование его в необходимый формат в html-файле. Я храню данные, которые мне нужны в этих вопросах, а также блок кода, который представлен каждым вопросом. Затем я выводя эти блоки в файл. Пока это хорошо работает. Что делает это плохой идеей и что вы предложите вместо этого? – Zannith

ответ

4

Сделайте базовый класс abstract, добавьте реферат WriteQuestion к базовому классу, а затем override его в каждой конкретной реализации.

+0

@ Эрик Липперт, спасибо за вашу помощь, я пометил суперкласс как абстрактный и создал абстрактный метод writequestion в суперклассе, теперь, когда я создаю переопределение записи в в подклассах говорится, что подходящий метод не найден для переопределения. Не могли бы вы добавить пример того, как он может работать в моем коде? – Zannith

+0

Вы сделали способ переопределить виртуальный? – Botonomous

+0

Хорошо, я получил эту часть работы, Спасибо за помощь! К ошибке, что исправление этого вызвано, конечно;) – Zannith

1

IMHO Я бы разделил ваш код на несколько классов, чтобы получить хорошее разделение проблем.

Ваш класс 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

@ Zannith Добро пожаловать! Завтра я буду настраивать даже такие элементы, как «TrueFalseQuestionConverter», вместо использования делегатов. –

+0

@ Zannith BTW У меня есть идея ... вам действительно нужно использовать XML? Сериализация JSON очень проста в использовании с JSON.NET. Вы можете очень упростить свой код, используя JSON вместо XML. Текстовый вопрос для объектов будет выполнен автоматически, вы выбросите «XmlQuestionConverter». –