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