C#專案—模擬考試

郭恩硕發表於2024-08-13

C#模擬考試軟體

開發了一個《模擬考試》的小軟體,此小軟體練習的目的主要是為了體會程式設計思想,深度理解高內聚、低耦合,掌握程式設計思維邏輯的大招,告別垃圾程式碼,重點體會程式設計之美,練習時長30分鐘;

開發一個專案之前,切記不要開啟程式就寫程式碼,首先要做的就是分析專案,從專案的架構開始思考,軟體要實現什麼功能(思考UI介面佈局);資料從哪裡獲取?(資料庫、文字檔案、通訊介面...);重點思考專案物件、功能有哪些?(物件的屬性<成員>、方法<功能>,之間的關係...),以此專案為例,思維導圖如下:

No1. 軟體實現的功能有哪些?(UI如何設計)

答: 用一個主介面實現,有標題欄(專案名稱及關閉按鈕)、選單欄(上一題、下一題、提交按鈕)、內容顯示題目,選項以勾選的方式答題,最後提交得出總分;

No2.資料在哪裡獲取?

答: 文字檔案(是否可靠,避免使用者更改原始檔,進一步最佳化<序列化儲存檔案後反序列化讀取>—用到的技能:文字檔案操作<後續總結概括>)

No3.物件有哪些,它們之間的關係是什麼?

答: 軟體執行強相關的物件有試卷、試題、答案(正確答案、錯誤答案);一張試卷中若干試題(一對多),試題在試卷中以集合的方式存在;試題的答案唯一(一對一),答案在試題中以物件屬性的形式存在;

No4.如何設計類(列出屬性<成員>、方法<功能>)?

答: 窗體載入試卷物件(試題載入(答案));從最底層答案類開始設計:


【答案類】
屬性:所選答案、正確答案;

/// <summary>
/// 答案類(屬性<成員變數>:所選答案、正確答案)
/// </summary>
[Serializable]  //實體類需序列化儲存
public class Answer
{
    public string SelectAnswer { get; set; } = string.Empty;
    public string RightAnswer { get; set; } = string.Empty;
}

【試題類】
屬性:題幹、選項、答案(物件屬性<需在類的構造方法初始化>);

/// <summary>
/// 試題類(屬性:題幹、答案<物件屬性-建構函式初始化>)
/// </summary>
[Serializable]
public class Question
{
    public Question()  //建構函式初始化
    {
        QueAnswer = new Answer();
    }
    public string Title { get; set; } = string.Empty;
    public string DescriptionA { get; set; } = string.Empty;
    public string DescriptionB { get; set; } = string.Empty;
    public string DescriptionC { get; set; } = string.Empty;
    public string DescriptionD { get; set; } = string.Empty;
    public Answer QueAnswer { get; set; }  //答案為物件屬性
}

【試卷類】
屬性:試題集合List
方法:計算總分、讀取文字(抽取試題);

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace WindowsFormsApp_試卷抽題.Models
{
    /// <summary>
    /// 試卷類(屬性:試題集合、  方法:抽題、分數)
    /// </summary>
    public class Paper
    {
        //建構函式初始化(物件屬性)
        public Paper()
        {
            questions = new List<Question>();
        }
        //欄位:試題物件集合
        private List<Question> questions;
        //屬性:只讀
        public List<Question> Questions { get { return this.questions; } }

        //方法:分數【遍歷試題選擇的答案與正確答案相等即可得分】
        public int Grade()
        {
            int score = 0;
            foreach (Question item in questions)
            {
                if (item.QueAnswer.SelectAnswer == string.Empty) continue;
                if (item.QueAnswer.SelectAnswer.Equals(item.QueAnswer.RightAnswer)) score += 5;
            }
            return score;
        }

        //方法:抽題:【初始化(1.讀檔案 2.儲存為序列化檔案 3.開啟序列化檔案)】
        //讀文字檔案
        public void OpenPaper1()
        {
            FileStream fs = new FileStream("questions.txt", FileMode.Open);
            StreamReader sr = new StreamReader(fs, Encoding.Default); //注意預設編碼方法
            string txtPaper = sr.ReadToEnd();  //讀取全部內容到txtPaper
            string[] txtQuestions = txtPaper.Trim().Split('&');  //分割內容儲存到試題陣列;注意去空格
            string[] txtQuestion = null; //儲存一道題到txtQuestion
            foreach (string item in txtQuestions)
            {
                txtQuestion = item.Trim().Split('\r');
                this.questions.Add(new Question
                {
                    Title = txtQuestion[0].Trim(),
                    DescriptionA = txtQuestion[1].Trim(),
                    DescriptionB = txtQuestion[2].Trim(),
                    DescriptionC = txtQuestion[3].Trim(),
                    DescriptionD = txtQuestion[4].Trim(),
                    QueAnswer = new Answer { RightAnswer = txtQuestion[5].Trim() }
                });
            }
            sr.Close();
            fs.Close();
        }

        //儲存為序列化檔案
        public void SavePaper()
        {
            FileStream fs = new FileStream("Paper.obj", FileMode.Create); //建立檔案
            BinaryFormatter bf = new BinaryFormatter();  //序列化器
            bf.Serialize(fs, this.questions);  //序列化物件
            fs.Close();
        }

        //開啟序列化檔案【儲存檔案後用此方法將獨到的資料傳遞到試題集合中即可】
        public void OpenPaper2()
        {
            FileStream fs = new FileStream("Paper.obj", FileMode.Open); //開啟檔案
            BinaryFormatter bf = new BinaryFormatter(); //序列化器
            this.questions = (List<Question>)bf.Deserialize(fs);  //反序列化到物件
            fs.Close();
        }
    }
}


【邊界類】
屬性:試卷物件;
方法:顯示題目、儲存答案、重置答案;

/// <summary>
/// 邊界類:試卷物件、題序  方法:顯示題目、儲存答案、重置答案
/// </summary>
public partial class FrmMain : Form
{
    //視窗初始化
    public FrmMain()
    {
        InitializeComponent();
        this.palCover.Visible = true;
        this.lblView.Text = "試卷密封中,等待抽題......";
    }

    //試卷物件、題序號
    private Paper Paper = new Paper();
    private int QuestionIndex = 0;

    //開始抽題
    private void btnStart_Click(object sender, EventArgs e)
    {
        this.palCover.Visible = false;
        //顯示第一題
        this.Paper.OpenPaper1();  //開啟txt檔案
        //this.Paper.SavePaper();  //儲存文序列化檔案
        //this.Paper.OpenPaper2();   //反序列化開啟試卷
        ShowPaper();

    }

    //提交試卷
    private void btnSubmit_Click(object sender, EventArgs e)
    {
        this.palCover.Visible = true;
        //儲存答案並判斷
        SaveAnswer();
        int score = this.Paper.Grade();
        //顯示成績
        this.lblView.Text = $"您的得分是: {score}  分";
    }

    //上一題
    private void btnUp_Click(object sender, EventArgs e)
    {
        if (QuestionIndex == 0) return;
        else
        {
            SaveAnswer();
            QuestionIndex--;
            ResetAnswer();
            ShowPaper();
        }
    }

    //下一題
    private void btnDown_Click(object sender, EventArgs e)
    {
        if (QuestionIndex == Paper.Questions.Count - 1) return;
        else
        {
            SaveAnswer();
            QuestionIndex++;
            ResetAnswer();
            ShowPaper();
        }
    }

    //顯示試題
    public void ShowPaper()
    {
        this.lblTiltle.Text = this.Paper.Questions[this.QuestionIndex].Title;
        this.lblA.Text = this.Paper.Questions[this.QuestionIndex].DescriptionA;
        this.lblB.Text = this.Paper.Questions[this.QuestionIndex].DescriptionB;
        this.lblC.Text = this.Paper.Questions[this.QuestionIndex].DescriptionC;
        this.lblD.Text = this.Paper.Questions[this.QuestionIndex].DescriptionD;
    }

    //儲存答案
    public void SaveAnswer()
    {
        string selecetAnswer = string.Empty;
        if (this.ckbA.Checked) selecetAnswer += "A";
        if (this.ckbB.Checked) selecetAnswer += "B";
        if (this.ckbC.Checked) selecetAnswer += "C";
        if (this.ckbD.Checked) selecetAnswer += "D";
        this.Paper.Questions[QuestionIndex].QueAnswer.SelectAnswer = selecetAnswer;
        
    }

    //重置答案
    public void ResetAnswer()
    {
        this.ckbA.Checked = this.Paper.Questions[this.QuestionIndex].QueAnswer.SelectAnswer.Contains("A");
        this.ckbB.Checked = this.Paper.Questions[this.QuestionIndex].QueAnswer.SelectAnswer.Contains("B");
        this.ckbC.Checked = this.Paper.Questions[this.QuestionIndex].QueAnswer.SelectAnswer.Contains("C");
        this.ckbD.Checked = this.Paper.Questions[this.QuestionIndex].QueAnswer.SelectAnswer.Contains("D");
    }

    //退出
    private void btnClose_Click(object sender, EventArgs e)
    {
        this.Close();
    }
}

軟體效果


思維導圖如下所示(重點體會思路,思路清晰程式碼就成了):

相關文章