@sulim000333

[C#] Представляю вам мой недо-тест) Как правильно написать этот код/как оптимизировать это «код»?

Не обращайте внимание на слишком тупо и неправильно написанный код, я только учусь и я хотел осуществить идею с мои уровнем знаний) Вопрос: Как сделать все тоже самое только чтобы было не гомнокод, а грамотно?))
using System;

namespace DefaultProject
{
	class MainClass
	{
		public static void Main (string[] args)
		{
			Console.WriteLine ("Всем привет! И это моя первая мини-программа для проверки ваших знаний языка программирования C#\nЕсли готовы пройти тест напишите Go, а если хотите выйти напишите Exit.");
			string st = Console.ReadLine ();
			switch (st) 
			{
			case "Go":
				Console.WriteLine ("1.Какая команда выводит текст в консоль? \n 1. if/else \n 2. System.out.println(); \n 3. Console.WriteLine(); \n 4. int x = 10;");
				string questionstr1 = Console.ReadLine ();
				int question1 = Convert.ToInt32 (questionstr1);
				if (question1 >= 0) {
					Console.WriteLine("2.Какой тип из списка целочисленный? \n 1. char \n 2. int \n 3. float \n 4. double");
					string questionstr2 = Console.ReadLine ();
					int question2 = Convert.ToInt32 (questionstr2);
					if (question2 >= 0) {
						Console.WriteLine ("3.Какой тип из списка строчный? \n 1. string \n 2. byte \n 3. char \n 4. decimal");
						string questionstr3 = Console.ReadLine ();
						int question3 = Convert.ToInt32 (questionstr3);
						if (question3 >= 0) {
							Console.WriteLine ("4.Какой тип переменной используется в коде: int a = 5? \n 1. Знаковое 32-бит целое \n 2. Знаковое 64-бит целое \n 3. Знаковое 8-бит целое \n 4. 1 байт");
							string questionstr4 = Console.ReadLine ();
							int question4 = Convert.ToInt32 (questionstr4);
							if (question4 >= 0) {
								Console.WriteLine ("5.Сколько условных конструкций есть в C#? \n 1 \n 2 \n 3 \n 4");
								string questionstr5 = Console.ReadLine ();
								int question5 = Convert.ToInt32 (questionstr5);
								if (question5 >= 0) {
									switch (question1) {
									case 3:
										Console.WriteLine ("На первый вопрос вы ответили: 3. Console.WriteLine(); \n Правильно!");
										break;
									default:
										Console.WriteLine ("Первый вопрос: Неправильный ответ!. Вы выбрали: " + question1);
										break;
									}
									switch (question2) {
									case 2:
										Console.WriteLine ("На второй вопрос вы ответили: 2. int \n Правильно!");
										break;
									default:
										Console.WriteLine ("Второй вопрос: Неправильный ответ!. Вы выбрали: " + question2);
										break;
									}
									switch (question3) {
									case 1:
										Console.WriteLine ("На третий вопрос вы ответили: 1. string \n Правильно!");
										break;
									default:
										Console.WriteLine ("Третий вопрос: Неправильный ответ!. Вы выбрали: " + question3);
										break;
									}
									switch (question4) {
									case 1:
										Console.WriteLine ("На четвертый вопрос вы ответили: 1. Знаковое 32-бит целое \n Правильно!");
										break;
									default:
										Console.WriteLine ("Четвертый вопрос: Неправильный ответ!. Вы выбрали: " + question4);
										break;
									}
									switch (question5) {
									case 3:
										Console.WriteLine ("На пятый вопрос вы ответили: 3 \n Правильно!");
										break;
									default:
										Console.WriteLine ("Пятый вопрос: Неправильный ответ!. Вы выбрали: " + question5);
										break;
										}
									if ((question1 != 3) && (question2 != 2) && (question3 != 1) && (question4 != 1) && (question5 != 3)) {
										Console.WriteLine ("Вы на все вопросы ответили неправильно! Тест пройден на 0% ");
									}
									if ((question1 == 3) && (question2 == 2) && (question3 == 1) && (question4 == 1) && (question5 == 3)) {
										Console.WriteLine ("Вы на все вопросы ответили правильно! Тест пройден на 100%! ");
									}
								}
							}
						}
					}
				}
				break;
			}
			Console.ReadKey ();
		}
	}
}
  • Вопрос задан
  • 273 просмотра
Решения вопроса 1
@Free_ze
Пишу комментарии в комментарии, а не в ответы
Первое, что нужно сделать - определить доменные сущности, с которыми будет работать программа. В вашем случае это:
  • тест, который состоит из вопросов;
  • вопрос, который состоит из текста и ответов (упорядоченные по индексам);
  • ответ, котрый состоит из текста и признака правильности.
Код
public class Test
{
    public IList<Question> Questions { get; set; }
}

public class Question
{
    public string Text { get; set; }
    public IList<Answer> Answers { get; set; }
}

public class Answer
{
    public string Text { get; set; }
    public bool IsRight { get; set; }
}



Необходимо декомпозировать задачу на атомарные подзадачи. Каждую подзадачу можно унести в отдельную функцию, чтобы ограничить сферы ответственности кода (в духе SOLID в общем и SRP в частности), чтобы обезопасить себя от "эффекта бабочки", когда небольшое изменение отдого кусочка кода в одной части приложения рушит остальную работу в другой части. Да и читать код будет проще, не путаясь в деталях реализации из огромной портянки неразрывного кода с неочевидными связями переменных. Получается примерно так:

  1. логика "главного меню";
  2. логика теста:
    1. создание контента теста;
    2. прохождение теста:
      1. распечатка вопроса;
      2. получение ввода пользователя;
      3. проверка ответа;
      4. подсчет и вывод статистики.



Затем необходимо сформировать структуру теста. Для этого можно создать функцию-фабрику, которая возвращает новый объект теста (данные хардкодятся как здесь, читаются из базы, ходят за ними в интернет... whatever, нас интересует само тестирование):
Код
public static Test CreateTest()
{
    return new Test
    {
        Questions = new List<Question>
        {
            new Question
            {
                Text = "Какая команда выводит текст в консоль?",
                Answers = new List<Answer>
                {
                    new Answer { Text = "if/else" },
                    new Answer { Text = "System.out.println();" },
                    new Answer { Text = "Console.WriteLine();", IsRight = true },
                    new Answer { Text = "int x = 10;" }
                }
            },
            new Question
            {
                Text = "Какой тип из списка целочисленный?",
                Answers = new List<Answer>
                {
                    new Answer { Text = "char" },
                    new Answer { Text = "int", IsRight = true },
                    new Answer { Text = "float" },
                    new Answer { Text = "double" }
                }
            }
            //,...
        }
    };
}


Пусть программа, как у вас, приветствует пользователя, а затем, пока он не введет какой-то из двух корректных выводов (регистронезависимо (...IngoreCase)), будет показывать приглашение с подсказкой. Если Go - вызываем создание теста и заходим в игру. Если игра завершилась или пользователь изначально ввел Exit - печатаем знаменитое Press any key... и выходим из программы.
Код
public static void Main()
{
    Console.WriteLine("Всем привет! И это моя первая мини-программа для проверки ваших знаний языка программирования C#");
    string input;
    
    do
    {
        Console.WriteLine("Если готовы пройти тест напишите Go, а если хотите выйти напишите Exit.");
        input = Console.ReadLine();

        if (input.Equals("Go", StringComparison.InvariantCultureIgnoreCase))
        {
            var test = CreateTest();
            PlayGame(test); // <----- Логика тестирования находится здесь
        }
    } while (   !input.Equals("Go", StringComparison.InvariantCultureIgnoreCase)
             && !input.Equals("Exit", StringComparison.InvariantCultureIgnoreCase));

    Console.WriteLine("Для выхода нажмите любую клавишу...");
    Console.ReadKey();
}


Для игры нам потребуется массив с номерами "проваленных" вопросов (которые пригодятся для статистики в конце - CalculateResult). Мы обходим все вопросы из теста, сначала выводя их текст с вариантами на экран (PrintQuestion), затем спрашивая юзера его выбор (GetUserChoice). Затем ответ проверяется (IsCorrectAnswer), если он неверен - добавляем номер ответа (i - это индекс массива с нуля, поэтому +1) в массив ошибок. После всех вопросов - подсчитываем и выводим статистику.
Код
private static void PlayGame(Test test)
{
    var mistakes = new List<int>();
    for (int i = 0; i < test.Questions.Count; ++i)
    {
        var question = test.Questions[i];
        PrintQuestion(question, i);

        int choice = GetUserChoice(question.Answers.Count);

        if (!IsCorrectAnswer(choice, question.Answers))
        {
            mistakes.Add(i + 1);
        }
    }

    CalculateResults(test.Questions.Count, mistakes);
}


Вопрос распечатывается элементарно: сначала его текст, затем варианты ответов, перед которыми ставиться табуляция (\t), чтобы сформировался отступ списка:

Код
private static void PrintQuestion(Question question, int questionIndex)
{
    Console.WriteLine($"{questionIndex + 1}. {question.Text}");

    for (int i = 0; i < question.Answers.Count; ++i)
    {
        Console.WriteLine($"\t{i + 1}. {question.Answers[i].Text}");
    }
}


Получение варинта юзера делается циклом аналогичным вводу Go/Exit в начале. Его завершением управляет переменная isCorrectInput, которая ставится равной true только в самом конце, когда значение успешно спарсится и пройдет проверку на валидность номера ответа (от 1 до максимального номера ответа):
Код
private static int GetUserChoice(int answersCount)
{
    bool isCorrectInput = false;
    int choice = -1;
    do
    {
        try
        {
            Console.WriteLine("Введите номер ответа:");
            string input = Console.ReadLine();
            choice = Convert.ToInt32(input);
            if (choice >= 1 && choice <= answersCount) isCorrectInput = true;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Неправильный формат ввода");
        }
    } while (!isCorrectInput);

    return choice;
}


Для проверки правильности сначала выделяем правильные ответы вопроса (rightAnswers), а потом сравниваем их порядковые индексы с выбранным пользователем индексом. Если хоть один ответ совпал (да, их может быть несколько) - вернется true:
Код
private static bool IsCorrectAnswer(int choice, IList<Answer> answers)
{
    var rightAnswers = answers.Where(x => x.IsRight);
    int chosenAnswerIndex = choice - 1;
    return rightAnswers.Any(x => answers.IndexOf(x) == chosenAnswerIndex);
}


Ну и вишенка - распечатка результатов. Если ошибок нет - все гуд, 100% решений. Если ошибки есть, то мы решаем простую пропорцию, вычисляя сколько процентов ошибочных ответов было дано, а затем вычитаем их из 100%, получая процент успеха.
Код
private static void CalculateResults(int questionsCount, IList<int> mistakes)
{
    if (mistakes.Count == 0)
    {
        Console.WriteLine("Вы на все вопросы ответили правильно! Тест пройден на 100%! ");
    }
    else
    {
        string invalidQuestionsString = string.Join(", ", mistakes.Select(x => $"#{x}"));
        int progressPercentage = 100 - (int)Math.Ceiling(100 * (double)mistakes.Count / questionsCount);
        Console.WriteLine($"Среди ваших ответов есть неправильные ({mistakes.Count}: {invalidQuestionsString})). Тест пройден на {progressPercentage}% ");
    }
}



ЗЫ Вы можете поиграть с кодом, попробовать добавить больше вопросов, принудительное прерывание после первого неправильного ответа, обработку множественных корректных ответов (добавлять или баллы за частичное угадывание), разнообразить типы вопросов и прочее.

Полный листинг на ideone
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
Therapyx
@Therapyx
Data Science
- текстовый фаил с вопросам и ответами или тоже самое в базе данных
- класс игра с контейнером классами вопросов (1, *). Класс вопросов с контейнером классами ответов(1,*). Класс ответ обязательно имеет булеан аттрибут правильного ответа (да, нет).
- При старте игры в классе игра считывается текстовый фаил, создаются и распарсиваются классы по контейнерам (array, list, map...)
- Дальше фантазия безгранична.
Ответ написан
@youkerni
Junior Unity3D developer
Представьте что в вашем тесте 1000 вопросов, а не 5. Вы немного устанете, мне кажется.

Ну а если из примитивных ошибок - что будет если в качестве ответа вам дадут строку, а не число?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через TM ID
Похожие вопросы
от 40 000 до 70 000 руб.
SLC7 Екатеринбург
от 50 000 до 90 000 руб.
Fmedia Санкт-Петербург
от 120 000 до 150 000 руб.
18 янв. 2019, в 12:37
1000 руб./за проект
18 янв. 2019, в 12:31
100000 руб./за проект