0. 前言
本文基于深圳大学管理学院2022年秋季web程序设计课程整理代码与期末考题。
1. 运算符重载
运算符重载是指在编程语言中,允许用户自定义运算符操作符号(例如 +、-、*、/等)在自定义类或类型中的行为。它是一种功能强大的特性,允许程序员使用标准运算符来执行自定义类或类型的操作,而不是必须调用显式的方法或函数来执行它们。
如:重载+号,实现 (2,4) = (1,3)+(1,1)
注意事项:运算符重载函数必须是public、static,表明其与类或结构相关联,而不是与实例相关联。
例题1
定义一个类,该类由实数与虚数构成,请重载运算符+,实现使用+号即可实现该类的加法运算,其中实数部分相加,虚数部分相加。
// 定义类Number,由实部real和虚部image构成
struct Number
{
int real;
int image;
public Number(int a, int b)
{
real = a;
image = b;
}
// 运算符重载
public static Number operator+(Number x, Number y)
{
Number temp = new Number(x.real + y.real, x.image + y.image);
return temp;
}
}
// 主程序
class Program
{
static void Main(string[] args)
{
Number a = new Number(2,3);
Number b = new Number(3, 5);
Number c = new Number();
c = a + b;
}
}
例题2
有两个电话簿,每个电话簿里记录了学生的编号、姓名、电话号码,通过重载运算符+号,实现电话簿相加即可合并为新的电话簿。
// 定义电话簿类
class Book
{
public int length;
public Person[] plist;
public Book(int n)
{
length = n;
plist = new Person[n];
}
// 重载运算符+
public static Book operator+(Book b1, Book b2)
{
Book temp = new Book(b1.length + b2.length);
int current = 0;
for (int i = 0; i < b1.length; i++)
{
temp.plist[current] = b1.plist[i];
current++;
}
for (int j = 0; j < b2.length; j++)
{
temp.plist[current] = b2.plist[j];
current++;
}
return temp;
}
}
// 定义Person类,包含编号、名称和电话号码
class Person
{
public int id;
public string name;
public string pnum;
public Person(int a, string b, string c)
{
id = a;
name = b;
pnum = c;
}
}
// 主程序
class Program
{
static void Main()
{
Person p1 = new Person(1, "Messi", "1233");
Person p2 = new Person(2, "John", "3412");
Person p3 = new Person(3, "Amy", "1233");
Person p4 = new Person(4, "Arena", "3412");
Book b1 = new Book(2);
b1.plist[0] = p1;
b1.plist[1] = p2;
Book b2 = new Book(2);
b2.plist[0] = p3;
b2.plist[1] = p4;
Book b3 = b1 + b2;
foreach (Person p in b3.plist)
{
Console.WriteLine(p.id);
Console.WriteLine(p.name + p.pnum);
}
}
}
2. 索引器
索引器(Indexer)是C#语言中的一种特殊的属性,它允许对象以数组的方式访问元素,实现对对象的下标操作。
下面是一个示例代码,它定义了一个名为MyArray的类,该类包含一个整型数组,并且实现了一个索引器,可以用数组下标的方式访问该数组的元素:
public class MyArray
{
private int[] arr = new int[10];
// 索引器的声明
public int this[int i]
{
get { return arr[i]; }
set { arr[i] = value; }
}
}
// 测试代码
MyArray myArray = new MyArray();
myArray[0] = 1;
myArray[1] = 2;
myArray[2] = 3;
Console.WriteLine(myArray[0]); // 输出1
Console.WriteLine(myArray[1]); // 输出2
Console.WriteLine(myArray[2]); // 输出3
在上面的代码中,我们定义了一个名为MyArray的类,该类包含一个整型数组,并且实现了一个索引器,它的访问器(getter和setter)允许我们以数组下标的方式访问该数组的元素。在测试代码中,我们创建了一个MyArray对象,并且使用索引器来设置和获取数组的元素。输出结果分别为1、2和3,证明索引器的访问是有效的。
需要注意的是:
- 索引器没有名称
- 索引器可以有多个参数,每个参数可以是不同的数据类型,从而实现多维数组的操作。
- 索引器还可以重载,允许我们定义多个索引器来支持不同类型或参数数量的下标操作。
3. 异构数组
异构数组(heterogeneous array)是指数组中可以存储不同数据类型的元素,而不像普通数组一样只能存储相同数据类型的元素。
用索引器+多态的知识可以构建异构数组
例1
把多种类型的动物放到一个数组里
class Isomerism
{
public Animal[] ani_list;
public Isomerism(int length)
{
ani_list = new Animal[length];
}
public Animal this[int x]
{
set
{
ani_list[x] = value;
}
get
{
return ani_list[x];
}
}
}
abstract class Animal
{
public abstract void Yell();
}
class Dog : Animal
{
public override void Yell()
{
Console.WriteLine("Doggie");
}
}
class Cat : Animal
{
public override void Yell()
{
Console.WriteLine("kitten");
}
}
class Program
{
static void Main()
{
Isomerism i = new Isomerism(2);
i.ani_list[0] = new Dog();
i.ani_list[1] = new Cat();
foreach (Animal a in i.ani_list)
{
a.Yell();
}
}
}
4. 委托
在C#中,委托被定义为一个类,它可以保存对一个或多个方法的引用,并可以调用这些方法。
4.1 简单委托
例题1
用委托执行不同的运算函数
using System;
// 定义运算函数类
class Operation
{
public static double Plus(double x, double y)
{
return x + y;
}
public static double Minus(double x, double y)
{
return x - y;
}
public static double Multiply(double x, double y)
{
return x * y;
}
public static double Divide(double x, double y)
{
return x / y;
}
}
class Program
{
// 定义名为dele_oper的委托类型
delegate double dele_oper(double x, double y);
static void Main()
{
// 创建dele_oper实例p
dele_oper p = new dele_oper(Operation.Divide);
p += new dele_oper(Operation.Minus);
p += new dele_oper(Operation.Multiply);
p += new dele_oper(Operation.Plus);
p -= Operation.Plus;
Console.WriteLine(p(2,3));
}
}
例题2
用John的嘴说Messi的话
using System;
namespace Delegate_Test
{
class Jane
{
public delegate void p();
public static void month(p x)
{
x();
}
}
class Messi
{
static public void Cry()
{
Console.WriteLine("呜呜呜");
}
}
class Program
{
static void Main()
{
Jane.month(Messi.Cry);
}
}
}
4.2 委托与消息循环
例1
学生睡觉,老师惩罚
// 定义学生类型
class Student
{
// 定义名为Stu_dele的委托类型
public delegate void Stu_dele();
// 定义消息
static public event Stu_dele sleep_message;
// 定义睡觉函数
public void Sleep()
{
Console.WriteLine("I'm asleep");
sleep_message();
}
}
// 定义老师类型
class Teacher
{
// 定义惩罚学生函数
public static void Deal_sleep()
{
Console.WriteLine("揍你");
}
}
class Program
{
static void Main()
{
// 消息连接
Student.sleep_message += Teacher.Deal_sleep;
Student.Sleep();
}
}
例2
带值消息循环:学生迟到,老师扣学生工资,学生反抗。
class Student
{
public delegate void Stu_dele(object x);
static public event Stu_dele Delay_message;
public int money;
public Student(int n)
{
money = n;
}
public void Delay()
{
Console.WriteLine("I'm late");
Delay_message(this);
}
public static void Punished_react()
{
Console.WriteLine("Damn");
}
}
class Teacher
{
public delegate void Tea_dele();
public static event Tea_dele Punish_message;
public static void Deal_late(object x)
{
Student s = (Student)x;
Console.WriteLine("当前工资{0}", s.money);
s.money = s.money - 100;
Console.WriteLine("迟到了要扣钱,剩下工资{0}", s.money);
Punish_message();
}
}
class Program
{
static void Main()
{
Student.Delay_message += Teacher.Deal_late;
Teacher.Punish_message += Student.Punished_react;
Student s = new Student(200);
s.Delay();
}
}
5. 线程
5.1 多方法实现线程
-
Thread
根据想要的线程数量创建多个Thread。
Thread t = new Thread(函数)
t.Start() -
Task
可以管理多个异步任务。
Task t = Task.run(()=>函数()) -
ThreadPool
可以重复使用线程,避免了每次创建线程带来的开销。
ThreadPool.QueueUserWorkItem(函数)
例1 Thread实现多线程
using System;
using System.Threading;
class Program
{
static void Main(string[] args)
{
Thread newThread = new Thread(DoWork);
newThread.Start();
// main thread continues to run
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Main thread: {0}", i);
}
}
static void DoWork()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Worker thread: {0}", i);
}
}
}
例2 Task实现多线程
using System;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Task task = new Task(DoWork);
task.Start();
// main thread continues to run
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Main thread: {0}", i);
}
}
static void DoWork()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Worker thread: {0}", i);
}
}
}
例3 ThreadPool实现多线程
using System;
using System.Threading;
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(DoWork, i);
}
// main thread continues to run
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Main thread: {0}", i);
}
}
static void DoWork(object state)
{
int workItem = (int)state;
Console.WriteLine("Worker thread: {0}", workItem);
}
}
5.2 线程&加锁
2个销售员一起卖100本书
class Bookshop
{
static public int num = 10000;
}
class Seller
{
static object locker = new object();
public void Sellbook(object x)
{
lock (locker)
{
while (Bookshop.num != 0)
{
if (Bookshop.num != 0)
{
Bookshop.num--;
Console.WriteLine(x+"剩下{0}本", Bookshop.num);
}
else
{
Console.WriteLine("买完了");
}
}
}
}
}
class Program
{
static void Main(string[] args)
{
Seller s1 = new Seller();
Seller s2 = new Seller();
Thread t1 = new Thread(s1.Sellbook);
Thread t2 = new Thread(s2.Sellbook);
t1.Start("s1");
t2.Start("s2");
}
}
6. 数据库连接
数据库连接的步骤通常包括以下几个方面:
-
引用数据库连接类库:为了使用数据库连接功能,需要在代码中引用相应的数据库连接类库,例如在C#中需要引用System.Data.SqlClient。
-
创建数据库连接对象:通过创建数据库连接对象,可以连接到数据库并执行相关操作。通常需要指定数据库的连接字符串、用户名和密码等参数。
-
打开数据库连接:打开数据库连接之前,需要确保数据库连接对象已经被创建并且已经指定了相应的连接参数。
-
执行数据库操作:连接成功之后,可以执行数据库的增删改查等操作。具体的操作方式可以使用SQL语句或者ORM框架来实现。
-
关闭数据库连接:当操作完成之后,需要关闭数据库连接以释放资源,同时确保连接被正确关闭,以防止内存泄漏等问题。
总之,数据库连接的步骤包括创建数据库连接对象、打开数据库连接、执行数据库操作和关闭数据库连接。具体的实现方式可以根据使用的编程语言和数据库类型来选择相应的类库和方法。
例1
通过DataReader和DataSet两种方法实现数据库连接并查询匹配的名字,并将结果显示在GridView控件中的示例代码(使用MySqlConnect对象)。
示例代码中的connectionString和MyTable替换为自己的数据库连接字符串和表名,并将其保存为.aspx.cs文件。同时,将一个名为txtSearch的文本框和一个名为btnSearch的按钮添加到Web表单中,并将GridView控件命名为gvResults。
6.1 DataReader方法
using System;
using System.Data.SqlClient;
using System.Web.UI.WebControls;
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnSearch_Click(object sender, EventArgs e)
{
string connectionString = "Data Source=myServerAddress;Initial Catalog=myDataBase;User ID=myUsername;Password=myPassword;";
string searchName = txtSearch.Text.Trim();
using (SqlConnection connection = new SqlConnection(connectionString))
{
string query = "SELECT * FROM MyTable WHERE Name LIKE @Name";
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("@Name", "%" + searchName + "%");
connection.Open();
SqlDataReader reader = command.ExecuteReader();
gvResults.DataSource = reader;
gvResults.DataBind();
}
}
}
6.2 DataSet方法
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.UI.WebControls;
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnSearch_Click(object sender, EventArgs e)
{
string connectionString = "Data Source=myServerAddress;Initial Catalog=myDataBase;User ID=myUsername;Password=myPassword;";
string searchName = txtSearch.Text.Trim();
using (SqlConnection connection = new SqlConnection(connectionString))
{
string query = "SELECT * FROM MyTable WHERE Name LIKE @Name";
SqlDataAdapter adapter = new SqlDataAdapter(query, connection);
adapter.SelectCommand.Parameters.AddWithValue("@Name", "%" + searchName + "%");
DataSet dataset = new DataSet();
adapter.Fill(dataset);
gvResults.DataSource = dataset;
gvResults.DataBind();
}
}
}
期末考题
-
题型
选择题(10*2)
代码题(80) -
题目
(1)选择题
索引器没有名称
连接远程对象
TreadPool.QueueUserWorkItem启动执行
委托类型(函数指针?虚拟?…)
(2)代码
① 异构数组,把动物、老师、学生、花瓶放到一起
② 运算符重载,电话簿相加,合在一起
③ 多线程,多个售货员卖书
④ 数据库连接。连接,实现click查询匹配(用到like),grid控件
(3)附加题
线索二叉树,定义结构、构建、遍历。