这学期选修了CSharp&.net这门课,因为学过C++所以做实验时感觉很亲切,开始不是那么生疏。但毕竟它是比C++,Java新的一种面向对象的编程语言,所以新加了许多新的特点。正如课程教材开头所说的,C#是Microsoft的核心技术,而C#又是.NET的核心语言。又如学C++时一样我是从写实验程序开始学习C#的,以下是我写的几个引我逐渐深入了解C#编程方法的实验程序及其心得体会。
一、C#课程实验四
实验要求定义1-2个你认为最能体现重载特性的类,并对其方法或运算符进行重载来验证及了解重载的基本特性,类中要能实现定长参数列表的方法重载和变长参数列表的方法重载,为自定义的类的运算符(+,—,*,/及++,――)实现重载并重载并应用自定义类型的true/false运算符,显式和隐式运算符。
在程序中我定义了MessageHandler和Vector两个类。MessageHandler类用来完成对要显示不同数据类型的消息的处理,而Vector类用来实现基本的向量平面运算。按照实验要求分为以下具体内容:
1. MessageHandler中定长参数列表的方法重载
算法:
定义并实现三个方法,分别为message(),message(char ch)和message(string msg)。后两个为参数分别为char,string定长类型的方法重载,根据不同类型的对象调用不同的重载函数,实现相应功能。
2. MessageHandler中变长参数列表的方法重载
算法:
通过使用params定义并实现一个方法重载,打印出一个字符串和一系列变长的整型类型的数据。如:
public void message(string msg, params int[] args)
{
Console.WriteLine(msg);
for (int ix = 0; ix < args.Length; ix++)
Console.Write("{0}", args[ix].ToString());
Console.WriteLine();
}
3. 对Vector类的运算符实现重载
算法:
根据各向量基本运算定义几种主要运算符的重载,最终返回相应值,这值是个向量或整数值。对于++,--定义时先假定为后缀,过后重写该定义返回部分从而区分前后缀。最后通过对v1的++v1,v1++,--v1,v1—的计算来实现该运算符重载。对于true,false运算符的重载,让true表示该向量横纵坐标均为0,false表示该向量横纵坐标均不为0,并配合对-号的重载实现来判断两个向量是否相等,相等输出两个向量坐标和相等提示,不相等则输出两个向量相减后的结果。其中区分前后缀开始我知道要新声明一个向量类型的对象,并让它的横纵坐标等于要做运算向量的横纵坐标,后对新向量的横纵坐标做自增自减运算并返回新向量作为结果,即这可以完成前缀自增和自减的运算。而后的主要问题就在于如何实现后缀。用override重写++,--的ToString,以此实现后缀。
如:
public static Vector operator ++(Vector v)
{
Vector _v = new Vector(v._x,v._y);
_v._x++;
_v._y++;
return _v;
}
public static Vector operator --(Vector v)
{
Vector _v = new Vector(v._x, v._y);
_v._x--;
_v._y--;
return _v;
}
public override string ToString()//区分前后缀
{
return this.ToString();
}
4. 在MessageHandler 中添加一个结构体Digit,并重载显式隐式运算符
算法:
在MessageHandler中单独添加一个结构体Digit,用显式转换Digit d = new Digit(b)重载显式运算符,用隐式转换d.value重载隐式运算符。通过对Digit变量和byte类型变量之间的显式隐式转换实现刚定义的显式隐式运算符。
最终实验运行结果如下:
二、C#课程实验5
实验要求合理设计并实现文本查询系统QTL(Query Text Language)的类层次体系,系统支持对单词的查找,支持对一元和二元逻辑运算符与单词结合的查找,支持由逻辑运算符与单词组成的逻辑表达式的查找。
在程序中我定义了一个总的控制类QTL,来调用其它方法完成查询全过程。其算法如下:
1、QTL类中主要方法:
(1)InputQuery()//用户输入要查询的表达式
用string query保存表达式,若接收字符串为q则退出程序。否则通过queryProcess方法处理表达式。
(2)Precede(string optr, string c) //各表达式中逻辑符号的优先级处理
根据EvaluateExpression_reduced算法,列出各运算符的优先级比较表,并根据表用if—else语句返回两运算符进行比较后的结果。
(3)queryProcess(string _query) //将用户的要查询的表达式入栈进行相应操作,利用EvaluateExpression_reduced算法
用两个栈(运算符栈,操作符栈),根据接收的表达式字符数组的各字符串的内容和运算符的比较,如果不是运算符则进操作符栈,是运算符栈则根据该运算符和栈顶运算符的优先级比较,入栈,出栈运算等操作。
(4)add_file()//读取文本
从一指定文本中读取文本的所有内容并保存到Arraylist类型的对象里,记录文本行数和字符数等基本信息。
(5)printResult()//打印结果
从最终的结果的行号集中提取行号,并打印相应行的具体内容。
2、具体完成查询部分类的级别表示如下:
QueryàNameQueryàNotQuery
àBinaryQueryàAndQuery
àOrQuery
Query类中定义和初始化保存文本数据,行号集的对象,属性Solution用于读写受保护的行号集对象Solution_set。
NameQuery类派生于Query,将文本数据转换为小写字符然后和要查询的表达式字符串进行比较,若存在将相应行号加入结果行号集。
NotQuery类派生于NameQuery,将所有行号集加入NotQuery的结果行号集,在通过NameQuery找到含有表达式字符串的结果行号,把这些行号从NotQuery的结果行号集中除去,即得最终结果。
BinaryQuery类派生于Query类,定义两个属性,分别完成对两个操作符的读和写。
AndQuery类派生于BinaryQuery类,若两个操作符的结果集中都有的行号则加入最终的结果集中
OrQuery类派生于BinaryQuery类,若左操作数的结果集中和右操作符结果集中不相同的加入最终结果集中,再加入所有的右操作符结果集中的行号到最终结果集中。
3、程序实现难点
其中程序的难点在于将用户的要查询的表达式入栈进行相应操作,利用EvaluateExpression_reduced算法。这是学习数据结构时学过的用栈实现表达式求值使用的算法。首先要确定算符间的优先关系。在这个实验中它的优先关系如下:
op2 op1 | && | || | ! | ( | ) | # |
&& | > | > | < | < | > | > |
|| | > | > | < | < | > | > |
! | > | > | > | < | > | > |
( | < | < | < | < | = |
|
) | > | > | > | < | > | > |
# |
|
|
|
|
|
|
说明:任意两个相继出现的算符op1和op2之间的优先关系表示为三种关系,op1<op2 则op1的优先权低于op2;op1=op2 则op1的优先权等于op2;op1>op2 op1的优先权高于op2。
由这个算法写实现程序如下:
public char Precede(string optr, string c)
{//各表达式中逻辑符号的优先级处理
if ((optr.Equals("&&") && (c.Equals("&&") || c.Equals("||") || c.Equals(")")||c.Equals("#")))
|| (optr.Equals("||") && (c.Equals("&&") || c.Equals("||") || c.Equals(")")||c.Equals("#")))
|| (optr.Equals("!") && (!c.Equals("(")))
|| (optr.Equals(")") && (!c.Equals("("))))
return '>';
else if((optr.Equals("(") && c.Equals(")")))
return '=';
else
return '<';
}
public void queryProcess(string _query)
{//将用户的要查询的表达式入栈进行相应操作,利用 EvaluateExpression_reduced算法
char [] separators = {' ','/t'};
string [] stringsplit = (_query.ToLower()).Split(separators);
string [] operators={"!","||","&&","(",")","#"};
Stack<Query> opnd = new Stack<Query>();//查询结果
Stack<string> optr = new Stack<string>();//操作符
Query b , a, c;//储存要计算的操作符
string theta;//储存计算时用的运算符
optr.Push("#");
int ix = 0;
string ch;
do
{
if (ix == stringsplit.Length)
ch = "#";
else
ch = stringsplit[ix];
if (!In(ch, operators))
{
opnd.Push(new NameQuery(ch));
ix++;
}
else
switch (Precede(optr.Peek(), ch))
{
case '<':
optr.Push(ch);//将优先级高的符 号进符号栈
ix++;
break;
case '='://脱括号"(",并接收下一个字符
optr.Pop();
ix++;
break;
case '>'://退栈并将运算结果入栈
theta = optr.Pop();
if (theta.Equals("!"))
{
c = opnd.Pop();//储存要做非运算的query
opnd.Push(new NotQuery(c));
}
else if (theta.Equals("||"))
{
b = opnd.Pop();
a = opnd.Pop();
opnd.Push(new OrQuery(a, b));
}
else if (theta.Equals("&&"))
{
b = opnd.Pop();
a = opnd.Pop();
opnd.Push(new AndQuery(a, b));
}
break;
}
}while(ix <= stringsplit.Length);//判断表达式有没有接收完
for (int i = 0; i < (opnd.Peek()).Solution.Count; i++)
{
queryResult.Add((int)(opnd.Peek()).Solution[i]);//存储行号集到queryResult
}
}
4、实验结论
最终成功实现了文本查询系统。开始进栈出栈处理从用户接收的表达式时没有想到算符优先法,按照自己的想法做总有考虑不周全的地方以至于一直没能调出程序。但一旦使用了该算法并根据实际情况实现并修改了算法的部分细节,原来的难点便一攻就破。一想到这儿,才觉得数据结构中包含了多少经典的算法,往往与我们将要解决的某一实际问题相似或相同。如果我们只是凭自己的脑袋解决可能需要很复杂的判断等才能完成,造成了程序代码的冗长难读。而联系了数据结构中的算法后,往往能是问题解决起来感觉简单明了,代码也短小易读。这次实验是一次C#程序编写方法和数据结构结合使用的宝贵体验。呵呵,这也使得我写完这个实验比其它实验更有成就感了。
总之,这学期的C#&.net学下来很有收获,不仅学会了C#语言的基本编程方法,尤其是C++没有的委托,接口,而且在完成实验中可以用C#又一次实现了数据结构中的算法。接下来的大作业做学生成绩管理系统,现在完成了数据库的连接,还有十多个界面和模块要完成,如果能完成好,能用C#真正完成一个小型项目的开发对C#的学习会有更大的进步。到那时也必将有更多的收获与体会吧。但我知道要真正熟练掌握CSharp&.NET,还需要今后更多的学习。