4.1面向对象的基本概念
面向对象方法的基本思想就是从所要解决的问题本身出发,尽可能运用人类的思维方式(如分析、抽象、分类、继承等),以实现现实时间中的事务为中心思考问题、认识我呢提,并根据这些食物的本质特征,把它们抽象白哦是为系统中的对象,作为系统的基本构成单位。这时的程序设计者的任务包括两个方面:一是设计对象;二是通知有关对象完成所需的任务。
4.1.1对象
客观时间中任何一个事务都可以看成一个对象(object),对象可以是自然物体(如汽车、房屋、狗),也可以是社会生活中的一种逻辑结构(如班级、部门、组织),甚至是一篇文章、一个图形、一项计划等都可以视作对象。对象是构成系统的基本单位,在实际社会生活中,人们都是在不同的对象中活动的,例如工人在生产车间上班,学生在教室上课等。
所谓面向对象就是针对问题本身去分析那些具体事务,然后进行描述。
对象=属性+行为(方法、操作)
4.1.2事件与方法
事件(Event)又称为消息(Message),表示一个对象A向另一个对象B发出的服务请求。当某个事件发生(即对象B接收到对象A的消息或服务请求)时,对象B开始执行操作,操作结束后将执行的结果返回给对象A。方法(Method)表示一个对象能完成的服务或执行的操作功能。
4.1.3类与对象
类(Class)表示具有相同属性和行为的一组对象的集合,为该类的所有对象提供统一的抽象描述。类时对像是对象的抽象,而对象时该类的一个特例,类与对象的关系时抽象与具体的关系。
4.1.4抽象、封装、继承与多态
1、抽象:(abstraction)是处理事务复杂性的方法,只关注与当前目标有关的方面,而忽略与当前目标无关的那些方面。
2、封装:(encapsulation)有两个方面的含义:一是将有关的数据和操作代码封装在一个对象中,形成一个基本单位,各个对象之间相对独立,互不干扰。二是将对象中某些部分对外部隐藏,即隐藏其内不细节,只留下少量接口,以便与外界联系,接收外界的消息。
3、继承:(inheritance)就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的属性和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
4、多态:(polymorphism)是指在基类中定义的属性或方法被派生类继承后,可以表现出不同的行为特征,对同一消息会做出不同的响应。
4.2类的定义与使用
4.2.1类的声明和实例化
1、类的声明
在C#中,声明类使用保留字class就行,示例如下:

class Student { //定义类的数据成员 public string name; public int age; //定义类的方法成员 public string GetMessage() { return string.Format("姓名:{0},年龄:{1}岁.",name,age); } }
2、类的实例
1)对象的定义与创建
定义对象的格式与定义简单变量的格式相同,其格式如下:
类名 对象名
例如:
Student a; //声明一个Student型的对象a
实例化的语法格式为:
对象名 =new 类名();
例如:
a = new Student(); //为a分配内存空间
声明时同事实例化语法格式为:
类名 对象名 =new 类名();
例如:
Student b = new Student(); //声明同时创建对象
2)类成员的访问
类成员有两种访问方式:一种是在类的内部访问,另一种是在类的外部访问。
在类的内部访问类的成员,表示一个类成员要使用当前类中的其他成员,可以直接使用成员名称,有时为了避免引起混淆,也可以采用如下形式。
this.类成员
其中,this表示当前对象,是C#的关键字。
例如:
class Student { //定义类的数据成员 public string name; public int age; //定义类的方法成员 public string GetMessage() { return string.Format("姓名:{0},年龄:{1}岁。",this.name,this.age); } }
在类的外部方位类的成员,可通过对象名类访问,包括读取或修改对象的数据值、调用对象的方法等。使用对象名访问其内部成员的一般形式如下:
对象名.类成员
例如:
a.name="令狐冲";
a.age=21;
string strMsg=a.GetMessage();
实例4-1定义Student类并实例化类的对象

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 第4章 { public partial class 实例4_1 : Form { public 实例4_1() { InitializeComponent(); } class Student { //定义类的数据成员 public string name; public int age; //定义类的方法成员 public string GetMessage() { return string.Format("姓名:{0},年龄:{1}岁。", this.name, this.age); } } private void 实例4_1_Load(object sender, EventArgs e) { Student a; //声明一个Student对象a a = new Student(); //为a分配内存空间 Student b = new Student(); //申明同时创建对象b a.name = "令狐冲"; //修改对象a的数据成员的值 a.age = 25; string strMsg = a.GetMessage(); //调用对象a的方法成员 lblShow.Text = strMsg; b.name = "郭靖"; //修改对象b的数据成员的值 b.age = 26; lblShow.Text += "\n" + b.GetMessage(); //调用对象b的方法成员 } } }
4.2.2类的可访问性
表4-1C#中访问修饰符
声明 | 含义 |
public | 表示公共的,即访问不受任何限制,允许跨程序集引用,可用来修饰类及其成员 |
internal | 表示内部的,即只允许在当前程序集内部使用,可修饰类及其成员 |
protected | 表示受保护的,即只允许该类及其派生类使用,只能修饰类的成员 |
private | 表示私有的,即只允许在该类的内部使用,不允许其他类访问,只用来修饰类的成员 |
protected internal | 表示仅限于当前程序集之中内部使用,只允许该类及其派生类使用只能用来修饰类的成员 |
在使用访问修饰符时,要注意以下几点。
(1)一个成员或类型只能由一个访问修饰符,使用protected internal组合时除外。
(2)命名空间上不允许使用访问修饰符,命名空间没有访问限制。
(3)如果未指定访问修饰符,则使用默认的可访问性,类的成员默认为private。
(4)类的可访问性只能时internal或public,默认为internal。
(5)访问修饰符只是控制类的外部对类成员的访问,类的内部对自己成员的访问不受其限制,即在类的内部可以访问所有的类成员。
4.2.3值类型与引用类型
从数据存储的角度,C#的数据类型可分为值类型(value type)和引用类型(reference type),其中值类型用于存储数据的值,引用类型用于存储对于实际数据的引用。
1、值类型
值类型变量直接包含其本身的数据,前面提到的简单类型(int、bool、char)、结构类型(struct)、枚举类型(enum)等都是值类型。
2、引用类型
与值类型变量不同,引用类型变量本身不包含数据,只是存储对数据的引用,数据保存在其他位置,数组、字符串、类和后面要介绍的接口、委托等都属于引用类型。
3、装箱和拆箱
C#将值类型转换为对象的操作称为装箱,而把对象转换为兼容的值类型的操作称为拆箱。
装箱例如:
int i = 100;
object x = i; //表示先创建一个object型的变量x,然后再把值类型变量i的值复制给它
拆箱例如:
int i = 100;
object x = i; //装箱正确
int j = x; //拆箱错误,拆箱操作只能显示转换
int k = (int)x; //拆箱正确
long c =(long)x; //拆箱错误,拆箱前后的数据类型应相同
4.3类的成员及其定义
4.3.1类的常量与字段成员
1、常量
常量的值是固定不变的。
符号常量使用const关键字,其形式如下:
[访问修饰符] const 数据类型 常量名 =常量的值;
例如:
public const double pi =3.1415926;
2、字段
字段表示类的成员变量,字段的值代表一个对象的数据状态。
其一般形式如下:
[访问修饰符] 数据类型 字段名;
例如:
public double r;
4.3.2类的属性成员
属性用来读取、修改或计算字段的值。
定义属性的一般形式如下:
[访问修饰符] 数据类型 属性名
{ get { //获取属性的代码,用return返回值 } set { //设置属性的代码,用value赋值 } }
实例 4-2 计算圆的面积

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 第4章 { public partial class 实例4_2 : Form { public 实例4_2() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Circle c = new Circle(); c.R = Convert.ToDouble(textBox1.Text); //此行代码只能用属性R,不能换成字段r label2.Text = string.Format("半径为{0}的圆的面积为:{1}",c.R,c.Area); } class Circle { const double pi = 3.1415926; private double r; //字段成员 public double R //可读、写属性 { get { return r; } set { if (value < 0) r = 0; else r = value; //value为C#保留字,代表外部赋给本属性的值 } } public double Area //只读属性 { get { return pi * R * R; } //此行代码也可将属性R换成字段r } } } }
4.3.3类的方法成员
1、方法的声明
声明方法的一般形式如下:
[访问修饰符] 返回值类型 方法名 ([参数列表])
{
语句;
... ...
[return 返回值;]
}
例如:
public int Sum(int a , int b) { int c =a + b; return c; }
2、方法的调用
2.1同一个类中
语法格式: 方法名(参数列表)
例如:
class Calculater { public int Sum(int a, int b) //被调用方 { return a + b; } public string Display(int x,int y) //调用方 { return string.Format("{0}+{1}={2}", x, y, Sum(x, y)); } }
因为Display方法和Sum方法在一个类里面,所以可以直接调用
2.2在类的外部调用方法
对象名.方法名(参数列表)
例如:
class Calculater { public int Sum(int a, int b) //被调用方 { return a + b; } } class User { public string Display(int x, int y) //调用方 { Calculater k = new Calculater(); //创建类的实例,即对象k return string.Format("{0}+{1}={2}", x, y, k.Sum(x, y)); //通过x调用Sum(x,y) } }
4.3.4类的构造函数
构造函数的一般形式如下:
public 构造函数名([参数列表])
{
[语句;]
}
默认构造函数如下:
public 构造函数名(){}
C#允许重新定义默认构造函数
例如:
public Student() { name ="不知道"; age=20; }
实例 4-3 实现四则运算

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 第4章 { public partial class 实例4_3 : Form { public 实例4_3() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { int a = int.Parse(textBox1.Text); int b = int.Parse(textBox2.Text); Calculater x = new Calculater(a, b); //实例化类,创建对象x并调用构造函数 label3.Text = "两数之和为" + x.add(); //调用方法add } private void button2_Click(object sender, EventArgs e) { int a = int.Parse(textBox1.Text); int b = int.Parse(textBox2.Text); Calculater x = new Calculater(a, b); label3.Text = "两数之差为" + x.subtract(); //调用subtract } private void button3_Click(object sender, EventArgs e) { int a = int.Parse(textBox1.Text); int b = int.Parse(textBox2.Text); Calculater x = new Calculater(a, b); label3.Text = "两数之积为" + x.multiply(); //调用multiply } private void button4_Click(object sender, EventArgs e) { int a = int.Parse(textBox1.Text); int b = int.Parse(textBox2.Text); Calculater x = new Calculater(a, b); label3.Text = "两数之商为" + x.divide(); //调用divide } } class Calculater { private int a; //字段成员 private int b; //字段成员 private int B { get { return b; } set { if (value == 0) b = 1; else b = value; } } public Calculater(int i, int j) //构造函数 { a = i; B = j; //通过属性B为字段b赋值,防止b的值为0 } public int add() //方法成员 { return a + b; } public int subtract() //方法成员 { return a - b; } public int multiply() //方法成员 { return a * b; } public int divide() //方法成员 { return a / B; //为防止被除数为0,最好不要使用字段b } } }
4.4方法的参数传递
4.4.1按值传递
实例4-4用值传参进行参数值交换

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 第4章 { public partial class 实例4_4 : Form { public 实例4_4() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) //调用方 { Swaper x = new Swaper(); //创建对象 int a = Convert.ToInt32(textBox1.Text); int b = Convert.ToInt32(textBox2.Text); lblShow.Text = x.Swap(a, b); //调用并传递参数,a和b是实参变量 lblShow.Text += string.Format("\n\n调用方已经调用完毕,a={0},b={1}", a, b); } } class Swaper { public string Swap(int x,int y) //被调方,其中x和y是形参 { int temp = x; x = y; y = temp; return string.Format("被调方交换形参之后:x={0},y={1}", x, y); } } }
4.4.2 按引用传参
实例4-5 用应用传参进行参数值交换

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 第4章 { public partial class 实例4_4 : Form { public 实例4_4() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) //调用方 { Swaper x = new Swaper(); //创建对象 int a = Convert.ToInt32(textBox1.Text); int b = Convert.ToInt32(textBox2.Text); lblShow.Text = x.Swap(ref a,ref b); //调用并传递参数,a和b是实参变量(此处为引用型传参) lblShow.Text += string.Format("\n\n调用方已经调用完毕,a={0},b={1}", a, b); } } class Swaper { public string Swap(ref int x, ref int y) //被调方,其中x和y是形参(此处为引用型传参) { int temp = x; x = y; y = temp; return string.Format("被调方交换形参之后:x={0},y={1}", x, y); } } }
4.4.3输出参数
实例4-6用输出参数求文件路径中的目录和文件名

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 第4章 { public partial class 实例4_6 : Form { public 实例4_6() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) //调用方 { string path = textBox1.Text; string dir, file; //作输出参数使用,不需要初始化 Analyzer a = new Analyzer(); //创建对象 a.SplitPath(path, out dir, out file); //调用方法,dir和file为输出参数 textBox2.Text = dir; textBox3.Text = file; } } class Analyzer { //功能:从文件路径中分离目录和文件名 public void SplitPath(string path, out string dir, out string filename) //被调方 { int i = path.LastIndexOf('\\'); //获取最后一个反斜杠的位置 dir = path.Substring(0, i); //最后一个反斜杠前的字符串是文件目录 filename = path.Substring(i + 1); //最后一个反斜杠后的字符串是文件名 } } }
该程序中,形参dir和file是输出型参数,实参dir和file分别接收 形式参数dir和file的输出。
用ref和out修饰的参数都是引用传参,在方法体内对参数的修改和赋值都会被保留到实参中,但两者在使用上是有一定的区别的。
(1)用ref修饰的参数,在传参前必须对实参明确赋初值。
(2)用out修饰的参数,在传参前不需要给实参赋初值,但对应的形参必须在赋值后才能使用,且在方法结束前必须完成赋值操作。
4.4.4引用类型的参数
实例 4-7 通过修改引用类型的形参来修改实参对象的数据。

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 第4章 { public partial class 实例4_7 : Form { public 实例4_7() { InitializeComponent(); } void change(Student one) //被调方,one为引用类型的形参变量 { one.name = "黄蓉"; one.age = 20; } private void button1_Click(object sender, EventArgs e) { string name = textBox1.Text; int age = int.Parse(textBox2.Text); Student x = new Student(name, age); //创建对象x lblShow.Text = "变化之前," + x.getInfo(); //输出对象x的信息 change(x); //调用语句,x为引用类型的实参变量 lblShow.Text += "变换之后," + x.getInfo(); //重新输出对象x的信息 } } class Student { public string name; //字段成员 public int age; //字段成员 public Student(string name,int age) //构造函数 { this.name = name; this.age = age; } public string getInfo() //方法成员 { return string.Format("姓名:{0},年龄:{1}.\n\n", name, age); } } }
4.4.5数组型参数
实例 4-8 求若干数的最大值

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 第4章 { public partial class 实例4_8 : Form { public 实例4_8() { InitializeComponent(); } int Max(int[] a) //形参不是params数组 { int k = 0; //k用来记录最大数的下标值 for(int i=0;i<a.Length;i++) { if (a[k] < a[i]) k = i; } return a[k]; } int Max2(params int[] a) //形参是params数组,实参可使用数据列表 { int k = 0; for(int i=0;i<a.Length;i++) { if (a[k] < a[i]) k = i; } return a[k]; } private void 实例4_8_Load(object sender, EventArgs e) { int[] x = new int[] { 4,7,1,3,2,8,6,5}; int n = Max(x); //调用方法,将实参数组x传递给形参数组 lblShow.Text = "所有数据保存在实参数组中,其中最大值是" + n; n = Max2(4, 7, 1, 3, 2, 8, 6, 5); //调用方法,把数据列表作为实参传递给形参数组 lblShow.Text += "\n\n所有数据直接以数据列表形式作为实参,其中最大值是" + n; } } }
在该程序中,Max方法的形参数组没有添加修饰符params,在调用时对应的实参必须为已初始化的数组对象;而Max2方法的形参数组添加了修饰符params,在调用时对应的实参可以是数据列表,但必须保证列表中数据的类型与形参数组的数据类型一致。
在使用params修饰符时,要注意以下几点。
(1)params关键字可以修饰任何类型的参数
(2)params关键字只能修饰一维数组
(3)不允许对params数组使用ref或out关键字
(4)每个方法只能有一个params数组
4.5方法的重载
4.5.1方法的重载
定义:允许用同一方法名定义多个方法,这些方法的参数个数或参数类型不同,这就是方法的重载(function overloading)。
要求:
(1)重载的方法名称必须相同。
(2)重载方法的形参个数或类型不能相同,否则将出现一个“已定义了一个具有相同参数类型的成员”的错误。
实例 4-9 利用方法重载实现两个整数,两个双精度数、三个整数中求最大值。

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 第4章 { public partial class 实例4_9 : Form { public 实例4_9() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { int a = Convert.ToInt32(textBox1.Text); int b = Convert.ToInt32(textBox2.Text); Maxer x = new Maxer(); label4.Text = "最大值:"+ x.Max(a, b); //调用 1 号重载方法 } private void button2_Click(object sender, EventArgs e) { double a = Convert.ToDouble(textBox1.Text); double b = Convert.ToDouble(textBox2.Text); Maxer x = new Maxer(); label4.Text= "最大值:" + x.Max(a, b); //调用 2 号重载方法 } private void button3_Click(object sender, EventArgs e) { int a = Convert.ToInt32(textBox1.Text); int b = Convert.ToInt32(textBox2.Text); int c = Convert.ToInt32(textBox3.Text); Maxer x = new Maxer(); label4.Text = "最大值:" + x.Max(a, b, c); //调用 3 号重载方法 } } } class Maxer { public int Max(int a,int b) //1)号重载方法 { return a > b ? a : b; } public double Max(double a,double b) //2)号重载方法 { return a > b ? a : b; } public int Max(int a, int b, int c) //3)号重载方法 { int max = a; if (max < b) max = b; if (max < c) max = c; return max; } }
4.5.2构造函数的重载
实例 4-10 利用构造函数重载实现不同对象实例化

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 第4章 { public partial class 实例4_10 : Form { public 实例4_10() { InitializeComponent(); } public class Student { private string name; private int age; public Student() : this("无名", 20) { } //午餐构造函数(默认构造函数) public Student(string name) : this(name, 20) { } //有一个参数的构造函数 public Student(string name, int age) //有两个参数的构造函数 { this.name = name; this.age = age; } public string getInfo() { return string.Format("姓名:{0},年龄:{1}岁。", this.name, this.age); } } private void button1_Click(object sender, EventArgs e) { Student a; if(textBox2.Text=="" ) { if (textBox1.Text == "") { lblShow.Text = "调用无参构造函数(默认构造函数):"; a = new Student(); lblShow.Text += a.getInfo(); } else { lblShow.Text = "调用有一个参数的构造函数:"; a = new Student(textBox1.Text); lblShow.Text += a.getInfo(); } } else { int age = Convert.ToInt32(textBox2); lblShow.Text = "调用有两个参数的构造函数:"; a = new Student(textBox1.Text, age); lblShow.Text += a.getInfo(); } } } }
4.6.2析构函数
析构函数主要用来回收类的实例所占用的资源,是以在类名前面加“~”的方式来命名的。在对象销毁之前,.NET的公共语音运行时会自动调用析构函数并使用垃圾回收器回收对象所占用的内存空间。
C#类的析构函数具有如下特点。
(1)只能对类使用析构函数,不能再结构中定义析构函数;
(2)一个类只能由一个析构函数
(3)无法继承或重载析构函数
(4)析构函数既没有修饰符,也没有参数
(5)在析构函数被调用时,.NET的公共语言运行时自动添加对基类Object,Finalize方法的调用,以清理现场,因此在析构函数中不能包含对Object.Finalize方法的调用。
析构函数的一般形式如下:
~函数名()
{
语句;
}
课后实例
1、计算两个坐标之间的距离
要求定义一个Point类,包括:
(1)两个私有字段表示两个坐标值。
(2)一个构造函数通过传入的参数对坐标值初始化。
(3)两个只读属性对坐标值的取值。
(4)一个方法用来计算P点坐标和自己的距离。
核心代码如下:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 练习 { public partial class 计算两点之间距离 : Form { public 计算两点之间距离() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { int x1, y1, x2, y2; x1 = Convert.ToInt32(textBox1.Text); y1 = Convert.ToInt32(textBox2.Text); x2 = Convert.ToInt32(textBox3.Text); y2 = Convert.ToInt32(textBox4.Text); Point p1 = new Point(x1, y1); Point p2 = new Point(x2, y2); textBox5.Text = p1.Distance(p2).ToString(); } class Point { private int x; private int y; public int X { get { return x; } } public int Y { get { return y; } } public double Distance(Point p) { return System.Math.Sqrt((this.X - p.X) * (this.X - p.X) + (this.Y - p.Y) * (this.Y - p.Y)); } public Point(int a,int b) { x = a; y = b; } } } }
效果如下:
2、显示当前时间并增加一秒功能
要求定义一个Time类,包括:
(1)三个私有字段表示时、分、秒。
(2)两个构造函数,一个通过传入的参数对时间初始化,一个获取系统当前的时间。
(3)三个只读属性对时、分、秒的读取。
(4)一个方法用于对秒增加1秒(注意六十进位的问题)。
核心代码如下:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 练习 { public partial class 显示当前时间并有加一秒功能 : Form { public 显示当前时间并有加一秒功能() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { //获取文本 int sec = int.Parse(textBox3.Text); int min = int.Parse(textBox2.Text); int hour = int.Parse(textBox1.Text); //计算进位 sec++; if (sec >= 60) { sec = sec % 60; min++; } if (min >= 60) { min = min % 60; hour++; } if (hour >= 24) { hour = min = sec = 0; } //输出文本 textBox1.Text = hour.ToString(); textBox2.Text = min.ToString(); textBox3.Text = sec.ToString(); } private void Form2_Load(object sender, EventArgs e) { Time time = new Time(); //输出当前时间 textBox1.Text = time.hour.ToString(); textBox2.Text = time.minute.ToString(); textBox3.Text = time.second.ToString(); } } class Time { public int hour { get; } public int minute { get; } public int second { get; } public Time() { hour = System.DateTime.Now.Hour; //获取系统当前的小时 minute = System.DateTime.Now.Minute; //获取系统当前的分钟 second = System.DateTime.Now.Second; //获取系统当前的秒 } public Time(int h, int m, int s) { hour = h; minute = m; second = s; } //public int Hour //{ // get // { // return hour; // } //} } }
效果如下:
3、计算学生的成绩
要求如下:
(1)能根据姓名查询指定学生的总成绩
(2)能统计全班学生的平均成绩
(3)能统计单科成绩最高分
(4)能统计全班前三名的名单
(5)能统计指定课程的不及格的名单
(6)能统计指定课程在不同分数段的学生人数百分比
核心代码:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 练习 { public partial class 成绩统计 : Form { public 成绩统计() { InitializeComponent(); } Student[] st = new Student[100]; Grade gr = new Grade(); int i = 0; public class Student { public string sno; public string name; public double chinese; public double math; public double english; public Student(string sno,string name,double chinese,double math,double english) { this.sno = sno; this.name = name; this.chinese = chinese; this.math = math; this.english = english; } public double Sum { get { return chinese + math + english; } } } public class Grade { Student[] stu = new Student[100]; //存放班级中的每个同学的信息 double[] sum2 = new double[100]; //存放每个同学的总成绩 int i = 0; //学生人数 public Grade() { } public void Add(Student s) { stu[i] = s; //添加每个学生到班级中 sum2[i] = stu[i].Sum; //保存每个学生的总成绩 i++; //学生人数加1 } //查询指定学生的总成绩 int x = 0; int k = 0; bool flag = false; public double getSum(string s) { for(x=0;x<i;x++) { if(stu[x].name==s) { k = x; flag = true; } } if (flag==true) { return sum2[k]; //如果该学生在班级中,则返回该学生的总成绩,否则,返回-1 } else { return -1; } } //统计全班同学的平均成绩(2) double avg = 0; public double getAverage() { for(int aa=0;aa<i;aa++) { avg += sum2[aa]; } return avg / i; } //统计语文成绩最高分(3-1) double maxChinese = 0; public double getChineseMax() { for(int z=0;z<i;z++) { if(stu[z].chinese >maxChinese) { maxChinese = stu[z].chinese; } } return maxChinese; } //统计数学成绩最高分(3-2) double maxMath = 0; public double getMathMax() { for(int z = 0; z < i; z++) { if (stu[z].math > maxMath) { maxMath = stu[z].math; } } return maxMath; } //统计英语成绩最高分(3-3) double maxEnglish = 0; public double getEnglishMax() { for(int z = 0; z < i; z++) { if(stu[z].english>maxEnglish) { maxEnglish = stu[z].english; } } return maxEnglish; } //可以统计全班前3名的名单(4)--根据总成绩数组sum2[]将所有学生即stu数组重新排序 public string getNames() { Student[] t = new Student[1]; //中间变量(通过Student类型的中间变量,根据每个学生的总成绩重新排列学生类的全部信息) t[0] = new Student("", "", 0, 0, 0); double t2 = 0; for(int xx = 0; xx < i - 1; xx++) { for(int yy=xx+1;yy<i;yy++) { if(sum2[yy]>sum2[xx]) { t2 = sum2[yy]; sum2[yy] = sum2[xx]; sum2[xx] = t2; t[0] = stu[yy]; stu[yy] = stu[xx]; stu[xx] = t[0]; } } } return "" + stu[0].name + "" + stu[1].name + "" + stu[2].name; } //可以指定课程不及格的学生名单 string md = ""; public string getStudentMenu(string s) { if(s=="语文") { for(int x=0;x<i;x++) { if(stu[x].chinese<60) { md += " " + stu[x].name; } } return " " + md; } else if(s=="数学") { for(int x=0;x<i;x++) { if(stu[x].math<60) { md += " " + stu[x].name; } } return " " + md; } else if (s == "英语") { for(int x =0;x<i;x++) { if(stu[x].english<60) { md += " " + stu[x].name; } } return " " + md; } else { return "不存在(您输入的课程名称不正确)"; } } //统计指定课程在不同分数段的学生人数百分比 public string getStudentBFB(string s) { if (s == "语文") { double yw1 = 0; double yw2 = 0; double yw3 = 0; double yw4 = 0; double yw5 = 0; for(int z=0;z<i;z++) { if(stu[z].chinese<=100&&stu[z].chinese>=90) { yw1++; } else if (stu[z].chinese < 90 && stu[z].chinese >= 80) { yw2++; } else if (stu[z].chinese < 80 && stu[z].chinese >= 70) { yw3++; } else if (stu[z].chinese < 70 && stu[z].chinese >= 60) { yw4++; } else { yw5++; } } return "\n90-100:" + (yw1 / i) * 100.0 + "%\n80-90:" + (yw2 / i) * 100.0 + "%\n70-80:" + (yw3 / i) * 100.0 + "%\n60-70:" + (yw4 / i) * 100.0 + "%\n60以下:" + (yw5 / i) * 100.0 + "%"; } else if (s == "数学") { double yw1 = 0; double yw2 = 0; double yw3 = 0; double yw4 = 0; double yw5 = 0; for(int z=0;z<i;z++) { if (stu[z].math <= 100 && stu[z].math >= 90) { yw1++; } else if (stu[z].math < 90 && stu[z].math >= 80) { yw2++; } else if (stu[z].math < 80 && stu[z].math >= 70) { yw3++; } else if (stu[z].math < 70 && stu[z].math >= 60) { yw4++; } else { yw5++; } } return "\n90-100:" + (yw1 / i) * 100.0 + "%\n80-90:" + (yw2 / i) * 100.0 + "%\n70-80:" + (yw3 / i) * 100.0 + "%\n60-70:" + (yw4 / i) * 100.0 + "%\n60以下:" + (yw5 / i) * 100.0 + "%"; } else if (s == "英语") { double yw1 = 0; double yw2 = 0; double yw3 = 0; double yw4 = 0; double yw5 = 0; for (int z = 0; z < i; z++) { if (stu[z].english <= 100 && stu[z].english >= 90) { yw1++; } else if (stu[z].english < 90 && stu[z].english >= 80) { yw2++; } else if (stu[z].english < 80 && stu[z].english >= 70) { yw3++; } else if (stu[z].english < 70 && stu[z].english >= 60) { yw4++; } else { yw5++; } } return "\n90-100:" + (yw1 / i) * 100.0 + "%\n80-90:" + (yw2 / i) * 100.0 + "%\n70-80:" + (yw3 / i) * 100.0 + "%\n60-70:" + (yw4 / i) * 100.0 + "%\n60以下:" + (yw5 / i) * 100.0 + "%"; } else { return "不存在(您输入的课程名称不正确)"; } } } //添加每个学生成绩 private void button1_Click(object sender, EventArgs e) { try { st[i] = new Student(textBox2.Text, textBox3.Text, Convert.ToInt32(textBox4.Text), Convert.ToInt32(textBox5.Text), Convert.ToInt32(textBox6.Text)); gr.Add(st[i]); i++; textBox1.Text = "成功添加" + i + "个学生的信息"; textBox2.Text = ""; textBox3.Text = ""; textBox4.Text = ""; textBox5.Text = ""; textBox6.Text = ""; } catch { MessageBox.Show("输入完善的学生信息进行添加!!!"); } } //点击完成按钮显示各种查询结果 private void button2_Click(object sender, EventArgs e) { textBox1.Text = ""; if (textBox8.Text.Trim() != "") { //计算全班学生的平均成绩 textBox1.Text += "\n\n全班同学的平均成绩为: " + Convert.ToString(gr.getAverage()); //统计单科成绩的最高分 textBox1.Text += "\n\n语文成绩最高分为:" + Convert.ToString(gr.getChineseMax()); textBox1.Text += "\n\n数学成绩最高分为:" + Convert.ToString(gr.getMathMax()); textBox1.Text += "\n\n英语成绩最高分为:" + Convert.ToString(gr.getEnglishMax()); //统计全班前三名的名单 textBox1.Text += "\n\n全班前三名的名单为:" + gr.getNames(); //指定课程不及格的学生名单 textBox1.Text += "\n\n指定科目“" + textBox8.Text + "”不及格的名单为" + gr.getStudentMenu(textBox8.Text); //统计指定课程在不同分数段的学生人数百分比 textBox1.Text+="\n\n指定科目“"+textBox8.Text+"”不同分数段的百分比如下:"+gr.getStudentBFB(textBox8.Text); textBox8.Text = ""; } else { MessageBox.Show("请输入您要查询的课程名称:"); } } //可以根据姓名查询指定学生的总成绩 private void button3_Click(object sender, EventArgs e) { textBox1.Text = ""; if(textBox3.Text.Trim()!="") { double result = gr.getSum(textBox3.Text); if (result == -1) { MessageBox.Show("该学生不存在!!!"); } else { textBox1.Text = "学生:" + textBox3.Text + "的总成绩为:" + Convert.ToString(result); textBox3.Text = ""; } } else { MessageBox.Show("请输入您要查询的学生姓名"); } } } }
效果如下:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?