Вопрос: Как правильно написать этот код/как оптимизировать это «код»?

Лучший ответ:
Первое, что нужно сделать - определить доменные сущности, с которыми будет работать программа. В вашем случае это:тест, который состоит из вопросов;вопрос, который состоит из текста и ответов (упорядоченные по индексам);ответ, котрый состоит из текста и признака правильности.Код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 в частности), чтобы обезопасить себя от "эффекта бабочки", когда небольшое изменение отдого кусочка кода в одной части приложения рушит остальную работу в другой части. Да и читать код будет проще, не путаясь в деталях реализации из огромной портянки неразрывного кода с неочевидными связями переменных. Получается примерно так:логика "главного меню";логика теста:создание контента теста;прохождение теста:		распечатка вопроса;		получение ввода пользователя;		проверка ответа;		подсчет и вывод статистики.
Затем необходимо сформировать структуру теста. Для этого можно создать функцию-фабрику, которая возвращает новый объект теста (данные хардкодятся как здесь, читаются из базы, ходят за ними в интернет... 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

Худший ответ:
- текстовый фаил с вопросам и ответами или тоже самое в базе данных 
- класс игра с контейнером классами вопросов (1, *). Класс вопросов с контейнером классами ответов(1,*). Класс ответ обязательно имеет булеан аттрибут правильного ответа (да, нет). 
- При старте игры в классе игра считывается текстовый фаил, создаются и распарсиваются классы по контейнерам (array, list, map...)
- Дальше фантазия безгранична.