How to avoid type checking in C# to load correct UI?

拜拜、爱过 提交于 2020-01-23 10:39:43

问题


I am building a simple quiz program with WinForms that has 2 modes:

1) Edit mode: Where the user can create its own questions

2) Quiz mode: Where the user needs to answer the questions

Currently there are 2 type of questions: Open (question and free text box) and multiple choice (question and 4 possible answers).

I have created an abstract class for a Question:

public abstract class Question
{
    public string QuestionString { get; private set; }
    public Question(string q)
    {
        QuestionString = q;
    }
}

And 2 sub classes that inherit this class:

public class OpenQuestion : Question
{
    public string CorrectAnswer { get; private set; }
    public OpenQuestion(string q, string a) : base(q)
    {
        CorrectAnswer = a;
    }
}
public class MultipleChoiceQuestion : Question
{
    public string[] Answers { get; private set; }
    public MultipleChoiceQuestion(string q, string[] ans) : base(q)
    {
        Answers = ans;
    }
}

I save all created questions in a List<Question>, and eventually in a text file as JSON.

My problems is loading a question, how can I load the correct UI?

For open question there would be a label with the question and text field to write the answer, and for Multiple choice question there would be the question label, and 4 radio buttons.

Currently this is what I do:

public void LoadNextQuestion()
{
    Question q = GetCurrentQuestion(); // Just returning the current question to show
    if(q is OpenQuestion) // And here is the problematic part.
    {
        ShowOpenQuestion(q as OpenQuestion); // Load UI for open question
    }
    else if(q is MultipleChoiceQuestion)
    {
        ShowMultipleChoiceQuestion(q as MultipleChoiceQuestion); // Load UI for Multiple choice question
    }
}

Is there a way to avoid this type checking? Because it looks wrong to me, if I would add another type of question I would need to go back to this method and add another condition for that type.

EDIT For the UI I use panels that holds the UI, for example:

public void ShowOpenQuestion(OpenQuestion oq)
{
    openQuestionPanel.Visible = true;
    multipleChoicePanel.Visible = false; // Hide other question type panels, currently there is only one more
    openQuestionLabel.Text = oq.QuestionString;
    openQuestionInputField.Text = string.Empty;
}

Thanks in advance


回答1:


Type-Checking is your friend. Do not fight type checking. There is even a movement to add/use more of it. At best you can get somebody elses code do the type checking for you (like the Function call resolution).

In your given example however, I do not see a lot of issues. If anything, it will be beneficial if you keep doing it yourself, because it will avoid issues down the line. I can not even think of a half dozen "Question Types" - so it is not like this list will be long by any measurement.

The first thing you could do, is repalce those if/elses with a switch/case. They were given pattern matching ability recently. And indeed, type based casting and processing was one of the first thing added. The example code is right around what you do right now. If you use switch, the default should be used to display some error message or even throw an exception. This is how you avoid future issue (forgetting to give a proper code for new classes. Somebody else messing up the inheritance by not giving the draw funciton).

Of course a switch/Case can usually be repalced with a Collection. Dictionary<type, DelegateThatTakesAnyQuestionInstance>. You have to do manual casting in the Delegate (unless co- or contravaraince help here?), but you know it matches. And with Lambdas instead of named functions for the Delegate, you could even hide this entire code somewhere with other large collections you defined. Again, no match should give you a notificaiton, up to exception.

You did not specify your programming Environment when I wrote this. But it is worth pointing out that if you use WPF/UWP and you follow MVVM, there is a nice helper for you: I call them "Type Targetting Data Tempaltes". If you throw some random class at the View and no higher order thing applies, it will try to find a matching Template to display it. Using the TargetType property.




回答2:


On a general level, the answer is to apply the object-oriented design principles of information hiding and encapsulation by:

  • having your abstract Question class specify an abstract method that renders the UI for a specific type of question and
  • overriding that method in each concrete subclass, i.e., OpenQuestion and MultipleChoiceQuestion.

On a more concrete level, the "exact" answer on how those methods should render the UI depends on the UI paradigm and framework you are using.

  • Regarding the UI paradigm, you need to decide on whether or not you are using MVVM (Model, View, ViewModel), MVC (Model, View, Controller), or other approaches.
  • Assuming this is about a desktop application, on the framework level, you have a choice between Windows Forms and WPF, both of which offer different ways to do this in a more or less elegant fashion. For MVVM, my choice would be WPF.
  • On top of the basic choice between Windows Forms and WPF, there are several UI frameworks or libraries (e.g., MVVMlight, to name just one) that offer powerful features to address the typical challenges of UI programming (e.g., events, commands).


来源:https://stackoverflow.com/questions/59116736/how-to-avoid-type-checking-in-c-sharp-to-load-correct-ui

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!