我如何在包含子类的超类列表上调用子类方法

how do i call a subclass method on a superclass list containing subclasses

我很难为我的标题措辞,所以现在让我试着详细说明一下。首先是我的相关代码:

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

所以我正在创建多种类型的问题,它们都是 "Question" 的子类。 Question 包含适用于每种类型问题的所有数据,这些 sub类 包含它们独有的方法,主要方法是 "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 的定义,即使它的每个 sub类 都包含。如果我应该添加更多内容以进行说明,请发表评论,因为我一直在反复修改它,所以我的代码有点混乱。我是像这样使用 类 的新手,所以我可能会遗漏一些关键概念,请指出您看到的任何内容,谢谢。

创建基础classabstract,在基础class中添加一个抽象WriteQuestion成员,然后在每个具体实现中override它。

恕我直言,我会将您的代码拆分成更多 classes 以获得良好的关注点分离。

您的 Question class 和专业化(即派生 classes)不应该知道它们是如何存储的,也不应该知道它们是如何存储的变成了某种格式,例如 HTML 表示形式。

我会定义一个名为 XmlQuestionConverter:

的 class
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 转换为问题列表,我们已准备好使用 HtmlTextWriter:

将它们转换为 HTML
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% 确定它是否会工作,但这是了解如何以清晰的方式实现代码的良好开端关注点分离!您可能需要做一些调整以使我的代码适应您的实际用例。