一、学前入门
1. .Net平台
.Net/DotNet:一般指.Net FrameWork框架,一种平台,一种技术。
为了方便理解,打个比方:
.Net平台 = 一个厨房
.Net FrameWork框架 = 柴米油盐酱醋茶各类调料、锅碗瓢盆各类工具
基于.Net平台开发出来的各种应用 = 一道道好吃/难吃的饭菜
总结:.Net FrameWrok框架是.Net平台中不可缺少的一部分,它提供了一个稳定的运行环境来保证我们基于.Net平台开发的各种应用能够正常的运转。
2. C#编程语言
C#(C Sharp):一种编程语言,可以开发基于.Net平台的应用。
3. .Net都能干什么
1.Winform应用程序(桌面应用程序)
2.Internet应用程序(ASP.NET)
3.手机开发(wp8开发)
4.Unity3D游戏开发/虚拟现实开发
4. .Net两种交互模式
1. C/S:客户端(Client)/服务器(Server)模式
2. B/S:浏览器(Browser)/服务器(Server)模式
5. VS的各个组成部分
解决方案、项目及类之间的关系:
解决方案 = 公司
项目 = 部门
类 = 员工
方法(函数)= 员工会的技能
Program中的各个组成部分:
引用命名空间;
方法(函数);
注意:Main函数是我们程序的主入口,你写的代码如果想要被执行的话,必须写在Main函数当中。
.sln:解决方案文件,里面包含着整个解决方案的信息,可以双击运行。
.csproj:项目文件,里面包含着这个项目的信息,可以双击运行。
6.书写代码需要注意的地方
(1)代码中出现的所有标点都是英文半角,Shift键快速切换中文半角和英文半角,
Shift+空格切换全角/半角。
(2)在C#代码中,每行代码的结束,我们都以分号结束,注意:这个分号也是英文半角的分号。
7.运行程序的两种方式
(1)点击绿色的启动按钮;
(2)使用快捷键F5。
8. VS中常用的快捷键
(1)Ctrl+K+D:快速对齐代码;
(2)Ctrl+Z:撤销;
(3)Ctrl+S:保存;
(4)Ctrl+J:快速弹出智能提示;
(5)Shift+End、Shift+Home
(6)Ctrl+K+C:注释所选代码;
(7)Ctrl+K+U:取消对所选代码的注释;
(8)F1:转到帮助文档;
(9)折叠冗余代码:#Region和#EndRegion;
二、基础语法
1.注释符
注释符的作用:
(1)用来注销代码段;
(2)用来解释代码段;
C#的三种注释符:
(1)单行注释 //
(2)多行注释 /* 要注释的内容 */
(3)文档注释 /// 多用来解释类或方法。
例1:
//这行代码的作用是将Hello World打印到控制台当中
Console.WriteLine("Hello World");
//这行代码的作用是暂停当前程序
Console.ReadKey();
例2:
/// <summary>
/// 这个方法的作用是:求两个整数之间的最大值
/// </summary>
/// <param name="n1">第一个整数</param>
/// <param name="n2">第二个整数</param>
/// <returns>返回比较大的那个数字</returns>
public static int GetMax(int n1,int n2)
{
return n1 > n2 ? n1 : n2;
}
例3:
/// <summary>
/// 这个类用来描述一个人的信息:姓名、年龄、性别
/// </summary>
public class Person
{
public string Name
{
get;
set;
}
public int Age
{
get;
set;
}
public char Gender
{
get;
set;
}
}
2. 变量
1.变量的作用
用来在计算机当中存储数据。
2.存储变量的语法
变量类型 变量名;
变量名=值;
注意:“=”号,在这里并不表示等于的意思,而是赋值的意思,表示把等号右边的值 赋给等号左边的变量。
例:
//官方语言:声明或定义了一个int类型的变量
int number;//在内存中开辟了一块能够存储整数的空间
//官方语言:给这个变量进行赋值
number = 100;//表示把100存储到了这块空间内
}
3.数据类型
类型 | 范围 | 大小 | .Net Framework类型 |
---|---|---|---|
int | -2147483648到2147483647 | 有符号 32位整数 | System.Int32 |
(1)整数类型:int 只能存储整数,不能存储小数。
(2)小数类型:double 既能存储整数,也能存储小数,小数点后面的位数为 15~16位。
(3)金钱类型:decimal 用来存储金钱,值后面需要加上一个m。
(4)字符串类型:string 用来存储多个文本,也可以存储空,字符串类型的值需要被双引号(英文半角)括起来。
(5)字符类型:char 用来存储单个字符,最多、最少只能有一个字符,不能存储空,字符类型的值需要用单引号(英文半角)括起来。
4. 变量的使用规范
如果你要是用变量的话,应该要先声明再赋值再使用;
5. 变量的命名规则
(1)必须以 字母、下划线_ 或@符号开头,不要以数字开头;
(2)后面可以跟任意 字母、数字、下划线。
注意:
(1)你起的变量名不要与C#系统中的关键字重复;
(2)在C#中,大小写是敏感的;
(3)同一个变量名不允许重复定义。
给变量起名字的时候要满足两个命名规范:
(1)Camel 骆驼命名规范,要求变量名称首单词的首字母要小写,其余每个单词的首字母要大写,多用于给变量命名;
(2)Pascal 命名规范,要求每个单词的首字母都要大写,其余字母小写,多用于给类或者方法命名。
6. 赋值运算符
= 表示赋值的意思,表示把等号右边的值,赋值给等号左边的变量。由等号连接的表达式称之为赋值表达式。例如: int number=10;
注意:每个表达式我们都可以求解除一个定值,对于赋值表达式而言,等号左边的变量的值就是整个赋值表达式的值。
7. +号的作用
(1)连接:当+号两边有一边是字符串的时候,+号就起到了连接的作用;
(2)相加:当两边都是数字的时候;
答案如下:
static void Main(string[] args)
{
//第一题答案
string name = "卡卡西";
string address = "火影村";
int age = 30;
string email = "kakaxi@qq.com";
int salary = 2000;
Console.WriteLine("我叫"+name+",我住在"+address+",我今年"+age+"了,我的邮箱是"+email+",我的工资是"+salary);
//第二题答案
int myAge = 18;
myAge = 81;
Console.WriteLine("这个人的年龄是:" + age + "岁");
}
8. 占位符
使用方法:先挖个坑,再填个坑。
使用占位符需要注意的地方:
(1)你挖了几个坑,就需要填上几个坑,如果你多填了,没效果;如果你少填了,就会抛异常;
(2)输出顺序:按照挖坑的顺序输出;
答案如下:
static void Main(string[] args)
{
//第三题答案
string name = "小明";
int age = 18;
char gender = '男';
string phoneNumber = "010-12345";
Console.WriteLine("我叫{0},我今年{1}岁了,我是{2}生,我的电话是{3}", name, age, gender, phoneNumber);
//第四题答案
string myAddress = "芒夏路19号";
int myAge = 15;
string myName = "丧彪";
Console.WriteLine("我家在{0},今年{1}岁了,我的姓名是{2}", myAddress, myAge, myName);
//第五题答案一
int number1 = 10;
int number2 = 5;
int temp;
temp = number1;
number1 = number2;
number2 = temp;
Console.WriteLine("交换后number1的值为{0},number2的值为{1}", number1, number2);
//第五题答案二
int numberA = 10;
int numberB = 5;
numberA = numberA + numberB;
numberB = numberA - numberB;
numberA = numberA - numberB;
Console.WriteLine("交换后numberA的值为{0},numberB的值为{1}", numberA, numberB);
}
9.接收用户的输入
static void Main(string[] args)
{
Console.WriteLine("请输入你的姓名");
//我们还想要接收你输入的姓名
string name = Console.ReadLine();
Console.WriteLine("您的姓名是{0}", name);
}
练习:请用户输入姓名、性别、年龄,当用户按下某个键后,在屏幕上显示:您好,xx您的年龄是xx,你是个x生。
答案如下:
static void Main(string[] args)
{
Console.WriteLine("请输入你的姓名:");
string name = Console.ReadLine();
Console.WriteLine("请输入你的性别:");
string gender = Console.ReadLine();
Console.WriteLine("请输入你的年龄");
string age = Console.ReadLine();
Console.WriteLine("你好{0},你的性别是{1},你的年龄是{2}", name,gender,age);
}
10. 转义字符和@符号的作用
转义符:指的是一个斜杠 ‘ \ ’ + 一个特殊的字符,组成了一个具有特殊意义的字符。
\n 表示:换行;
\" 表示:一个英文半角的双引号;
\t 表示:一个Tab键的空格;(多用于排版)
\b 表示:一个退格键,放到字符串的两边没有效果;
\r\n 表示:Windows操作系统不认识\n,只认识\r\n;
\\ 表示:一个\;
@符号作用:
1.取消斜杠\在字符串中的转义作用,使其单纯的表示为一个\;
2.将字符串按照编辑的原格式输出;
11. 强制类型转换
答案如下:
static void Main(string[] args)
{
//练习2答案
int r = 5;
double area_Circle = MathF.PI * r * r;
double girth_Circle = MathF.PI * r * 2;
Console.WriteLine("该圆的面积为{0},周长为{1}", area_Circle, girth_Circle);
//练习3答案
int price_Tshirt = 35;
int price_Trousers = 120;
double price_Total = (3 * price_Tshirt + 2 * price_Trousers)*0.88;
Console.WriteLine("客户应该支付{0}元", price_Total);
}
1. 隐式类型转换:
我们要求等号两边参与运算的操作数的类型必须一致,如果不一致,满足以下条件就会发生自动类型转换,或者称之为隐式类型转换。
(1)两种类型相兼容;例如:int 和double兼容(都是数字类型)
(2)目标类型大于源类型;例如:double > int
2. 显式类型转换:
(1)两种类型相兼容; 例如:int 和double兼容(都是数字类型)
(2)大的转成小的; double--int
3. Convert类型转换:
如果两个类型的变量不兼容,比如string与int或者string与double,这个时候我们可以使用一个叫做Convert的转换工厂进行转换。
例如:string s="123"; double d=Convert.ToDouble(s); Console.WriteLine(d);
注意:使用Convert进行类型转换,也需要满足一个条件:面子上一定得过得去。
例如下面的强制转换就不行:string s="123abc"; double d=Convert.ToDouble(s); Console.WriteLine(d); 这个123abc就说不过去。
三、运算符
1. 算术运算符
++: 分为前++和后++,不管是前++还是后++,最终的结果都是给这个变量加一;
--: 分为前--和后--,不管是前--还是后--,最终的结果都是给这个变量减一;
区别表现表达式当中,
如果是前++/前--,则先给这个变量自身加一/减一,然后带着这个加一/减一后的值参与运算;
如果是后++/前--,则先拿原值参与运算,运算完成后,再将这个变量自身加一/减一;
对于像++或--这样只需要一个操作数就能完成的运算,我们称之为一元运算符;
+ - * / % 对于这些需要两个或两个以上才能完成运算的操作符,我们称之为二元运算符。
注意:一元运算符的优先级要高于二元运算符,所以在一个表达式当中,如果既有一元运算符 又有二元运算符,我们首先计算一元运算符。
练习1:
static void Main(string[] args)
{
int a = 5;
int b = a++ + ++a * 2 + --a + a++;
Console.WriteLine(a);
Console.WriteLine(b);
}
输出结果为:
练习2:
static void Main(string[] args)
{
int var1,var2 = 5, var3 = 6;
var1 = var2++ * --var3;
//var1 = ++var2 * var3--;
Console.WriteLine(var1);
}
分别输出的结果为:
25
36
2. 关系运算符
6种关系运算符:> < >= <= == !=
关系运算符是用来描述两个事物之间的关系。
由关系运算符连接的表达式称之为关系表达式。
3. 逻辑运算符
3种逻辑运算符:&&(与)、| |(或)、!(非)
逻辑运算符两边放的一般都是关系表达式或者bool类型的值。
由逻辑运算符连接的表达式称之为逻辑表达式。
练习1:
让用户输入语文和数学成绩,输出以下判断是否正确,正确输出true,错误输出false:
(1)输入的语文和数学成绩都大于90分;
(2)语文和数学有一门是大于90分的。
答案如下:
static void Main(string[] args)
{
Console.WriteLine("请输入语文成绩:");
double score_Chinese = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("请输入数学成绩:");
double score_Math = Convert.ToDouble(Console.ReadLine());
bool isTrue;
//isTrue = score_Chinese > 90 && score_Math > 90;
isTrue = score_Chinese > 90 || score_Math > 90;
Console.WriteLine(isTrue);
}
练习2:
判断闰年?(闰年的判定需符合以下两个条件中任意的一个即可:1. 年份能够被400整除;2. 年份能够被4整除但是不能被100整除)
static void Main(string[] args)
{
Console.WriteLine("请输入要判断的年份:");
int year = Convert.ToInt32(Console.ReadLine());
bool isRunNian;
isRunNian = (year% 400 == 0||(year%4==0&&year%100!=0));
Console.WriteLine(isRunNian);
}
注意:这里即便不用括号,逻辑也是对的,因为逻辑与的优先级要高于逻辑或。
4.复合赋值运算符
+= -= *= /= %=
四、流程控制
顺序结构:程序从Main函数进入,从上到下 一行一行的执行,不会落下任何一行;
分支结构:if if—else
选择结构:if if—else switch—case
循环结构:while do—while for foreach
1.if语句
语法:
if(判断条件)
{
要执行的代码;
}
判断条件:一般为关系表达式或者bool类型的值。
执行过程:程序运行到if处,首先判断if所带的小括号中的判断条件,如果条件成立(也就是返回true),则执行if所带的大括号中的代码,如果判断条件不成立(也就是返回false),则跳过if结构,继续向下执行。
if结构特点:先判断,再执行,有可能一行代码都不执行。
2. if—else
语法:
if(判断条件)
{
执行的代码;
}
else
{
}
执行过程:程序执行到if处,首先判断if所带的小括号中的判断条件是否成立,如果成立(也就是返回true),则执行if所带的大括号中的代码,执行完成后,跳出if-else结构。
如果if判断条件不成立(返回false),则跳过if语句,执行else所带的大括号中的语句,执行完成后,跳出if-else结构。
if-else 特点:先判断,再执行,最少要执行一条代码。
答案如下:
static void Main(string[] args)
{
//练习1
Console.WriteLine("请输入密码:");
string password = Console.ReadLine();
if (password == "88888")
{
Console.WriteLine("密码输入正确!");
}
else
{
Console.WriteLine("密码输入错误,请再次输入:");
password = Console.ReadLine();
if (password == "88888")
{
Console.WriteLine("密码输入正确!");
}
else
{
Console.WriteLine("输入错误,程序结束");
}
}
//练习2
//Console.WriteLine("请输入用户名:");
//string account = Console.ReadLine();
//Console.WriteLine("请输入密码:");
//string password = Console.ReadLine();
//if(account=="admin"&&password=="88888")
//{
// Console.WriteLine("输入正确!");
//}
//else if(account!="admin")
//{
// Console.WriteLine("用户名不存在");
//}
//else if (account == "admin"&&password!="88888")
//{
// Console.WriteLine("密码输入错误");
//}
//练习3
Console.WriteLine("请输入年龄:");
int age = Convert.ToInt32(Console.ReadLine());
if(age>=18)
{
Console.WriteLine("可以查看");
}
else if(age<10)
{
Console.WriteLine("不允许查看");
}
else
{
Console.WriteLine("是否继续查看?(yes/no)");
string shiFouChaKan = Console.ReadLine();
if(shiFouChaKan=="yes")
{
Console.WriteLine("请查看");
}
else
{
Console.WriteLine("退出,您已放弃查看");
}
}
}
3. 异常捕获
语法上没有错误,在程序运行的过程当中,由于某些原因程序出现了错误,不能再正常运行。
我们在程序中经常会出现各种各样的异常,你如果想要你的程序变得坚强一些,在你的代码中就应该经常性的使用try-catch来进行异常捕获。
哪段代码你觉得有可能出现异常,你就踹他一脚。
语法:
try
{
可能会出现异常的代码;
}
//try和catch之间不能有其他的代码
catch
{
出现异常后要执行的代码;
}
执行过程:如果try中的代码没有出现异常,那么catch中的代码就不会执行。
如果try中的代码出现了异常,哪怕这行出现异常的代码后面还有一百行 都不会执行了,而是直接跳到catch中执行代码。
例子:
static void Main(string[] args)
{
bool isNormal = true;
int number = 0;
Console.WriteLine("请输入一个数字:");
try
{
number = Convert.ToInt32(Console.ReadLine());
}
catch
{
Console.WriteLine("异常!!输入的内容不能够转换成数字!");
isNormal = false;
}
//我们如果要执行下面这行代码,需要满足某些条件
//让代码满足某些条件去执行的话,使用bool类型
if (isNormal)
{
Console.WriteLine("程序正常执行,未发生异常");
}
}
4. Switch-case结构
用来处理多条件的定值判断。
语法:
switch(变量或者表达式的值)
{
case 值1:要执行的代码;
break;
case 值2:要执行的代码;
break;
case 值3:要执行的代码;
break;
........
default:要执行的代码;
break;
}
执行过程:程序执行到switch处,首先将括号中变量或者表达式的值计算出来,然后拿着这个值依次跟每个case后面所带的值进行匹配,一旦匹配成功,则执行该case所带的代码,执行完成后,遇到break,跳出switch-case结构。
如果跟每个case所带的值都不匹配,就看当前这个switch-case结构中是否存在default,如果有default,则执行default中的语句,如果没有default,则该switch-case什么都不做。
练习:
请用户输入年份,再输入月份,输出该月的天数。
static void Main(string[] args)
{
try
{
Console.WriteLine("请输入年份:");
int year = Convert.ToInt32(Console.ReadLine());
try
{
Console.WriteLine("请输入月份:");
int month = Convert.ToInt32(Console.ReadLine());
switch (month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
Console.WriteLine("本月共31天");
break;
case 4:
case 6:
case 9:
case 11:
Console.WriteLine("本月共30天");
break;
case 2:
if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0))
{//闰年
Console.WriteLine("本月共29天");
}
else
{//平年
Console.WriteLine("本月共28天");
}
break;
default:
Console.WriteLine("输入的月份不符合实际,程序退出");
break;
}
}//try月份结尾
catch
{
Console.WriteLine("月份输入有误,程序退出");
}
}//try年份结尾
catch
{
Console.WriteLine("年份输入有误,程序退出");
}
}
5. 循环结构
(1)while循环
语句:
while(循环条件)
{
循环体;
}
执行过程:程序运行到while处,首先判断while所带的小括号内的循环条件是否成立,如果成立的话,也就是返回一个true,则执行循环体,执行完一遍循环体后,再次回到循环条件进行判断,如果依然成立,则继续执行循环体,如果不成立,则跳出while循环。在while循环当中,一般总会有那么一行代码,能够改变循环条件,使之终有一天不再成立,如果没有那么一行代码能够改变循环条件,也就是循环条件永远都成立,我们称这种循环叫做死循环。
最简单、最常用的死循环:
while(true)
{
}
练习:求1-100之间所有整数的和。
static void Main(string[] args)
{
int i = 1;
int sum = 0;
while(i<=100)
{
sum += i;
i++;
}
Console.WriteLine("1-100之间整数的和为:" + sum);
}
(2)do-while循环
语法:
do
{
循环体;
}
while(循环条件);
执行过程:程序首先会执行do中的循环体,执行完成后,去判断do-while循环的循环条件,如果成立,则继续执行do中的循环体,如果不成立,则跳出do-while循环。
特点:先循环,再判断,最少执行一遍循环体。
6.程序调试
作用:
(1)写完一段程序后,想看一下这段程序的执行过程;
(2)当你写完这段程序后,发现程序没有按照你想象的样子去执行。
调试方法:
(1)F11 逐语句调试(单步调试)
(2)F10 逐过程调试
(3)断点调试
7. break
1. 可以跳出switch-case结构;
2. 可以跳出当前循环;
注意:break一般不单独的使用,而是跟着if判断一起使用,表示当满足某些条件的时候,就不再循环了。
答案如下:
static void Main(string[] args)
{
//练习2:
//Console.WriteLine("请输入班级人数:");
//int count = Convert.ToInt32(Console.ReadLine());
//int i = 0;
//double score_Average, score_Sum = 0;
//while (i < count)
//{
// Console.WriteLine("请依次输入每位学员的成绩:");
// double score = Convert.ToDouble(Console.ReadLine());
// score_Sum += score;
// i++;
//}
//score_Average = score_Sum / count;
//Console.WriteLine("该班级总分为:{0},平均分为:{1}", score_Sum, score_Average);
//练习3
//int count_Teach = 0;
//while (count_Teach < 10)
//{
// Console.WriteLine("这道题你们会做了吗?(yes/no)");
// string answer = Console.ReadLine();
// if (answer == "yes")
// {
// break;
// }
// count_Teach++;
//}
//Console.WriteLine("放学");
//练习4
double number = 80000;
int year = 2006;
while(number<=200000)
{
number *= 1.25;
year++;
}
Console.WriteLine("在{0}年,学员人数达到20万人", year);
}
8. Continue(s)
立即结束本次循环判断条件,如果成立,则进入下一次循环,否则退出循环。
就好比:1.运动员跑步喝水;2.程序员编写代码的时候突然遇到bug。
练习1:用while continue实现计算1到100(含)之间的除了能被7整除之外所有整数的和。
static void Main(string[] args)
{
int i = 1, sum = 0;
while(i<=100)
{
if(i%7==0)
{
i++;
continue;
}
sum += i;
i++;
}
Console.WriteLine(sum);
}
练习2:找出100以内所有的素数。
static void Main(string[] args)
{
int i = 2, sum = 0;
while (i <= 100)
{
int j = 1;
int count = 0;
while (j < i)
{
if(i%j==0)
{
count++;
}
j++;
}
if(count==1)
{
sum += i;
}
i++;
}
Console.WriteLine(sum);
}
9. for循环
语法:
for(表达式1;表达式2;表达式3)
{
循环体;
}
表达式1一般为声明循环变量,记录循环次数(int i=0;)
表达式2一般为循环条件(i<10)
表达式3一般为改变循环条件的代码,使循环条件终有一天不再成立(i++)
执行过程:程序首先执行表达式1,声明了一个循环变量用来记录循环的次数,然后执行表达式2,判断循环条件是否成立,如果表达式2返回的结果为true,则执行循环体,当执行完循环体后,执行表达式3,然后执行表达式2继续判断循环条件是否成立,如果成立则继续执行循环体,如果不成立,则跳出for循环。
练习1:求1-100之间的所有偶数和。
static void Main(string[] args)
{
int sum_OuShu = 0;
for(int i=1;i<=100;i++)
{
if(i%2==0)
{
sum_OuShu += i;
}
}
Console.WriteLine("1-100间的所有偶数的和为:" + sum_OuShu);
}
练习2:找出100-999之间的水仙花数。(水仙花数:每个位置上的数字的3次幂之和等于这个数本身)
static void Main(string[] args)
{
for(int i=100;i<=999;i++)
{
if(Math.Pow(i/100,3)+Math.Pow(i/10%10,3)+Math.Pow(i%10,3)==i)
{
Console.WriteLine("{0}是一个水仙花数",i);
}
}
}
练习3:输出九九乘法表。
static void Main(string[] args)
{
for(int i=1;i<=9;i++)
{
for(int j=1;j<=i;j++)
{
Console.Write("{0}x{1}={2} ", j, i, i * j);
}
Console.WriteLine();
}
}
10. 三元表达式
语法:
表达式1 ? 表达式2 : 表达式3;
表达式1一般为一个关系表达式。
如果表达式1的值为true,那么表达式2的值就是整个三元表达式的值;
如果表达式1的值为false,那么表达式3的值就是整个三元表达式的值。
注意:表达式2的结果类型必须跟表达式3的结果类型一致,并且也要跟整个三元表达式的结果类型一致。
static void Main(string[] args)
{
//计算两个数字的大小,求出最大的树
Console.WriteLine("请输入第一个数字:");
int num1 = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("请输入第二个数字:");
int num2 = Convert.ToInt32(Console.ReadLine());
int max = num1 > num2 ? num1 : num2;
Console.WriteLine("最大值为:" + max);
}
产生随机数:
static void Main(string[] args)
{
//产生随机数
//1.创建能够产生随机数的对象
Random r = new Random();
//2.让产生随机数的这个对象调用方法来产生随机数
int randomNum = r.Next(1, 10);
Console.WriteLine("产生的随机数为:" + randomNum);
}
五、复杂数据类型
1.Const常量
声明常量的语法:
const 变量类型 变量名=值;
注意:const int number = 10; //常量不能够被重新赋值
2.Enum枚举
语法:
[public] enum 枚举名
{
值1,
值2,
.......
}
public:访问修饰符,公开的公共的,哪儿都可以访问;
enum:关键字,声明枚举的关键字;
枚举名:要符合Pascal命名规范。
将枚举声明到命名空间的下面,类的外面,表示这个命名空间下,所有的类都可以使用这个枚举。
枚举其实就是一个变量类型(int / double / string / decimal),
只是枚举声明、赋值、使用的方式跟那些普通的变量类型不一样。
枚举类型和 string及int类型之间的转换:
public enum QQState
{
OnLine=2,
OffLine,
Leave=5,
Busy,
QMe
}
classProgram
{
static void Main(string[] args)
{
#region 将枚举类型强转成int类型
QQState state1 = QQState.OnLine;
//枚举类型默认可以跟int类型互相转换,枚举类型跟int类型是兼容的
int n1 = (int)state1;
Console.WriteLine(n1);
Console.WriteLine((int)QQState.OffLine);
Console.WriteLine((int)QQState.Leave);
Console.WriteLine((int)QQState.Busy);
Console.WriteLine((int)QQState.QMe);
#endregion
#region 将int类型强转为枚举类型
int n2 = 3;
QQState state2 = (QQState)n2;
Console.WriteLine(state2);
#endregion
}
}
注意:所有的类型都能够转换成string类型,调用ToString()即可:
public enum QQState
{
OnLine=2,
OffLine,
Leave=5,
Busy,
QMe
}
class Progaram
{
static void Main(string[] args)
{
//所有的类型都能够转换成string类型
int n1 = 10;
double n2 = 3.14;
decimal n3 = 5000m;
QQState state = QQState.OnLine;
Console.WriteLine(n1.ToString());
Console.WriteLine(n2.ToString());
Console.WriteLine(n3.ToString());
Console.WriteLine(state.ToString());
}
}
将string类型转换成枚举类型:
public enum QQState
{
OnLine=2,
OffLine,
Leave=5,
Busy,
QMe
}
class Progaram
{
static void Main(string[] args)
{
//将string类型转换成枚举类型
string s = "0";//Conver.ToInt32() int.Parse() int.TryParse()
//调用Parse()方法的目的就是为了让它帮助我们将一个字符串转换成对应的枚举类型
QQState state = (QQState)Enum.Parse(typeof(QQState), s);
Console.WriteLine(state);
}
}
总结:我们可以将一个枚举类型的变量跟int类型和string类型互相转换。
枚举类型默认是跟 int类型相互兼容的,所以可以通过强制类型转换的语法互相转换。
当转换一个枚举中没有的值的时候,不会抛异常,而是直接将数字显示出来。
枚举同样也可以跟string类型互相转换,如果将枚举类型转换成string类型,则直接调用ToString();
如果将字符串转换成枚举类型则需要下面这样一行代码:
(要转换的枚举类型)Enum.Parse(typeof(要转换的枚举类型),“要转换的字符串”);
如果转换的字符串是数字,则就算枚举中没有,也不会抛异常;
如果转换的字符串是文本,如果枚举中没有,则会抛出异常。
练习:
提示用户选择一个在线状态,我们接收,并将用户的输入转换成枚举类型,再次打印到控制台上。
using System;
public enum QQState
{
OnLine=1,
OffLine,
Leave,
Busy,
QMe
}
class Progaram
{
static void Main(string[] args)
{
//练习:提示用户选择一个在线状态,我们接收,并将用户的输入转换成枚举类型,再次打印到控制台上。
Console.WriteLine("请选择您的qq在线状态:1--OnLine 2--OffLine 3--Leave 4--Busy 5--QMe");
string input = Console.ReadLine();
switch(input)
{
case "1":
QQState s1 = (QQState)Enum.Parse(typeof(QQState), input);
Console.WriteLine("您选择的在线状态是{0}", s1);
break;
case "2":
QQState s2 = (QQState)Enum.Parse(typeof(QQState), input);
Console.WriteLine("您选择的在线状态是{0}", s2);
break;
case "3":
QQState s3 = (QQState)Enum.Parse(typeof(QQState), input);
Console.WriteLine("您选择的在线状态是{0}", s3);
break;
case "4":
QQState s4 = (QQState)Enum.Parse(typeof(QQState), input);
Console.WriteLine("您选择的在线状态是{0}", s4);
break;
case "5":
QQState s5 = (QQState)Enum.Parse(typeof(QQState), input);
Console.WriteLine("您选择的在线状态是{0}", s5);
break;
}
}
}
3. Struct结构
结构的作用:可以帮我们一次性声明多个不同类型的变量。
语法:
[public] struct 结构名
{成员;//字段
}
注意:变量在程序运行期间只能存储一个值,而字段可以存储多个值。
举例如下:
public struct Person
{
public string _name;//字段,前面加个下划线(区别于变量)
public int _age;
public Gender _gender;
}
public enum Gender
{
男,
女
}
class Progaram
{
static void Main(string[] args)
{
Person zsPerson;
zsPerson._name = "张三";
zsPerson._age = 21;
zsPerson._gender = Gender.男;
Person lsPerson;
zsPerson._name = "李四";
zsPerson._age = 18;
zsPerson._gender = Gender.女;
}
}
答案如下:
public struct MyColor
{
public int _red;
public int _green;
public int _blue;
}
public enum Gender
{
boy,
girl
}
public struct Person
{
public string name;
public Gender gender;
public int age;
}
class Progaram
{
static void Main(string[] args)
{
//练习1:
MyColor mc;
mc._red = 255;
mc._blue = 0;
mc._green = 0;
//练习2:
Person zsPerson;
zsPerson.name = "张三";
zsPerson.gender = Gender.boy;
zsPerson.age = 18;
Person xlPerson;
xlPerson.name = "小兰";
xlPerson.gender = Gender.girl;
xlPerson.age = 16;
}
}
4. Array数组
数组的作用:一次性存储多个相同类型的变量。
语法:
数组类型[] 数组名 = new 数组类型[数组长度];
如:int[] nums=new int[10];
当你写了上面这样一行代码后,就在内存中开辟了连续的10块空间,我们管每一个块称之为这个数组的元素。
如果你想要访问到数组中某一块元素,需要通过这个元素的下标或者索引去访问。
注意:数组的长度一旦固定了,就不能再被改变了。
//数组的声明方式:
int[] nums1 = new int[10];
int[] nums2 = { 1, 2, 3, 4, 5 };
int[] nums3 = new int[3] { 1, 2, 3 };
int[] nums4 = new int[] { 1, 2, 3, 4, 5 };
练习1:从一个整数数组中取出最大的整数、最小整数、总和、平均值。
static void Main(string[] args)
{
int[] array = new int[5] {1,2,3,4,5 };
int min=array[0], max=array[0], sum=0, average = 0;
for(int i=0;i<array.Length;i++)
{
if(min>=array[i])
{
min = array[i];
}
if(max<=array[i])
{
max = array[i];
}
sum += array[i];
average = sum / array.Length;
}
Console.WriteLine("该数组最小值是{0},最大值是{1},总和是{2},平均值是{3}", min, max, sum, average);
}
练习2:数组里面都是人的名字,分割成例如:老杨|老苏|老邹...(老杨,老苏,老邹,老虎,老牛,老王,老马)
string[] array = {"老杨","老苏","老邹","老虎","老牛","老马","老王"};
for(int i=0;i<array.Length;i++)
{
Console.Write(array[i] + "|");
}
练习3:将一个字符串数组的元素的顺序进行反转。{“我”,“是”,“好人”} {“好人”,“是”,“我”},第i个和第length-i-1个进行交换。
static void Main(string[] args)
{
string[] array = {"我","是","帅","哥"};
for(int i=0;i<array.Length/2;i++)
{
string temp = array[i];
array[i] = array[array.Length - 1 - i];
array[array.Length - 1 - i] = temp;
}
for(int i=0;i<array.Length;i++)
{
Console.Write(array[i]);
}
}
5.冒泡排序法
将一个数组中的元素按照从大到小或者从小到大的顺序进行排列。
static void Main(string[] args)
{
int[] array = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
//Array.Sort(array);//只能对数组升序
//Array.Reverse(array);//反转数组(数组降序就可以:先升序后反转)
for (int i = 0; i < array.Length - 1; i++)
{
for (int j = 0; j < array.Length - 1 - i; j++)
{
if (array[j] > array[j + 1])
{
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
for (int i=0;i<array.Length;i++)
{
Console.WriteLine(array[i]);
}
}
六、函数(方法)
1. 函数简介
函数就是将一堆代码进行重用的一种机制。函数就是一段代码,这段代码可能有输入的值(参数),可能会返回值。一个函数就像一个专门做这件事的人,我们调用它来做一些事情,它可能需要我们提供一些数据给它,它执行完成后可能会有一些执行结果给我们,要求的数据就叫参数,返回的执行结果就是返回值。
函数的语法:
[public] static 返回值类型 方法名([参数列表])
{
方法体;
}
public:访问修饰符,公开的,公共的,哪都可以访问;
static:静态的;
返回值类型:如果不需要写返回值,写void;
方法名:Pascal 每个单词的首字母都大写,其余字母小写;
参数列表:完成这个方法所必须要提供给这个方法的条件。如果没有参数,小括号也不能省略。
return的作用:1.在方法中返回要返回的值;2.立即结束本次方法。
方法写好后,如果想要被执行,必须要在Main函数中调用;
方法的调用语法:类名.方法名([参数])
在某些情况下,类名是可以省略的,如果你写的方法跟Main()函数同在一个类中,这个时候
class Program
{
static void Main(string[] args)
{
//计算两个整数之间的最大值
int max = Program.GetMax(1, 3);
Console.WriteLine(max);
}
/// <summary>
/// 计算两个整数之间的最大值并且将最大值返回
/// </summary>
/// <param name="n1">第一个整数</param>
/// <param name="n2">第二个整数</param>
/// <returns>将最大值返回</returns>
public static int GetMax(int n1,int n2)
{
return n1 > n2 ? n1 : n2;
}
}
如:string s=Console.ReadLine()就是一个有返回结果的函数;
Console.WriteLine("hello")就是一个有执行参数的函数,只有告诉WriteLine被打印的数据,它才知道如何打印;
int i=Convert.ToInt32("22");则是一个既有参数又有返回值的函数。
有了函数写代码就像拼积木,C#中的各种各样的技术其实就是通过for、if等这些基础的语法将不同的函数按照一定的逻辑组织起来。
/// <summary>
/// 判断一个年份是否是闰年
/// </summary>
/// <param name="year">要判断的年份</param>
/// <returns>是否是闰年</returns>
public static bool IsRun(int year)
{
bool b = (year % 400 == 0 || year % 4 == 0 && year % 100 != 0);
return b;
}
注意:不管是形参还是实参,它们都是在内存中开辟了空间的。
练习1:读取输入的整数,定义为方法,多次调用(如果用户输入的是数字,则返回,否则提示用户重新输入)。
/// <summary>
/// 这个方法需要判断用户的输入是否是数字
/// 如果是数字,则返回
/// 如果不是数字,提示用户重新输入
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static int GetNumber(string s)
{
while(true)
{
try
{
int number = Convert.ToInt32(s);
return number;
}
catch
{
Console.WriteLine("请重新输入");
s = Console.ReadLine();
}
}
}
练习2:计算输入数组的和:int Sum(int[] values)
/// <summary>
/// 计算一个整数类型数组的总和
/// </summary>
/// <param name="nums">要求总和的数组</param>
/// <returns>返回这个数组的总和</returns>
public static int GetSum(int[] nums)
{
int sum = 0;
for(int i=0;i<nums.Length;i++)
{
sum += nums[i];
}
return sum;
}
2. out参数
//写一个方法,求一个数组的最大值、最小值、总和、平均值
/// <summary>
/// 计算一个数组中的最大值、最小值、总和、平均值
/// </summary>
/// <param name="nums"></param>
/// <returns></returns>
public static int[] GetMaxMinSumAvg(int[] nums)
{
int[] res = new int[4];
//假设res[0]为最大值、res[1]为最小值、res[2]为总和、res[3]为平均值
res[0] = nums[0];
res[1] = nums[0];
res[2] = 0;
for(int i=0;i<nums.Length;i++)
{
//如果当前循环到的元素比我假定的最大值还大
if(nums[i]>res[0])
{
//将当前循环到的元素赋值给我的最大值
res[0] = nums[i];
}
if(nums[i]<res[0])
{
res[1] = nums[i];
}
res[2] += nums[i];
}
res[3] = res[2] / nums.Length;
return res;
}
如果在一个方法中,返回多个相同类型的值的时候,可以考虑返回一个数组。
但是,如果返回多个不同类型的值的时候,返回数组就不行了,那么这个时候,我们可以考虑使用out参数。
out参数就侧重于在一个方法中可以返回多个不同类型的值。
/// <summary>
/// 计算一个数组中的最大值、最小值、总和、平均值
/// </summary>
/// <param name="nums">数组</param>
/// <param name="max">多余返回的最大值</param>
/// <param name="min">多余返回的最小值</param>
/// <param name="sum">多余返回的总和</param>
/// <param name="avg">多余返回的平均值</param>
public static void GetMaxMinSumAvg(int[] nums,out int max,out int min,out int sum,out int avg)
{
//out参数要求在方法的内部必须为其赋值
max = nums[0];
min = nums[0];
sum = 0;
for(int i=0;i<nums.Length;i++)
{
if(nums[i]>max)
{
max = nums[i];
}
if(nums[i]<min)
{
min = nums[i];
}
sum += nums[i];
}
avg = sum / nums.Length;
}
class Program
{
static void Main(string[] args)
{
int num;
bool b = int.TryParse("123abc", out num);
Console.WriteLine(num);
Console.WriteLine(b);
}
public static bool MyTryParse(string s,out int result)
{
result = 0;
try
{
result = Convert.ToInt32(s);
return true;
}
catch
{
return false;
}
}
}
3. ref参数
将一个变量带入一个方法中进行改变,改变完成后,再将改变后的值带出方法。
ref参数要求在方法外必须对其赋值,而方法内可以不赋值。
static void Main(string[] args)
{
double salary = 5000;
JiangJin(ref salary);
Console.WriteLine(salary);
}
public static void JiangJin(ref double s)
{
s += 500;
}
static void Main(string[] args)
{
//使用方法来交换两个int类型的变量
int n1 = 10;
int n2 = 20;
Change(ref n1, ref n2);
Console.WriteLine(n1);
Console.WriteLine(n2);
}
public static void Change(ref int n1,ref int n2)
{
int temp = n1;
n1 = n2;
n2 = temp;
}
4. params参数
将实参列表中 跟可变参数数组类型一致的元素 都当做数组的元素去处理。
static void Main(string[] args)
{
Sum("张三", 99, 88, 77, 66, 55);
}
public static void Sum(string name,params int[] score)
{
int sum = 0;
for(int i=0;i<score.Length;i++)
{
sum += score[i];
}
Console.WriteLine("{0}这次考试的总成绩是{1}", name, sum);
}
params可变参数必须是形参列表中的最后一个元素。
static void Main(string[] args)
{
Sum("张三", 99, 100, 100, 100);
}
public static void Sum(string name,int id,params int[] score)
{
int sum = 0;
for(int i=0;i<score.Length;i++)
{
sum += score[i];
}
Console.WriteLine("{0}这次考试的总成绩是{1},学号是{2}", name, sum, id);
}
5. 方法的重载
概念:方法的重载指的就是方法的名称相同,但是参数不同。
参数不同,分为两种情况:
(1)如果参数的个数相同,那么参数的类型就不能相同;
(2)如果参数的类型相同,那么参数的个数就不能相同。
注意:方法的重载跟返回值没有关系。
public static void M(int n1, int n2)
{
int result = n1 + n2;
}
public static double M(double d1,double d2)
{
return d1 + d2;
}
public static void M(int n1,int n2,int n3)
{
int result = n1 + n2 + n3;
}
public static string M(string s1,string s2)
{
return s1 + s2;
}
6. 方法的递归
方法自己调用自己。
略。
练习1:/*提示用户输入两个数字,计算这两个数字的和。
1.用户只能输入数字;
2.要求第一个数字必须比第二个数字小,否则就重新输入;
3.计算两个数字的和;*/
static void Main(string[] args)
{
/*提示用户输入两个数字,计算这两个数字的和。
1.用户只能输入数字;
2.要求第一个数字必须比第二个数字小,否则就重新输入;
3.计算两个数字的和;*/
Console.WriteLine("请输入第一个数字:");
string strNumberOne = Console.ReadLine();
int numberOne = GetNumber(strNumberOne);
Console.WriteLine("请输入第二个数字:");
string strNumberTwo = Console.ReadLine();
int numberTwo = GetNumber(strNumberTwo);
//判断第一个数字是否小于第二个数字
JudgeNumber(numberOne, numberTwo);
//求和
int sum = GetSum(numberOne, numberTwo);
Console.WriteLine(sum);
}
public static int GetNumber(string s)
{
while(true)
{
try
{
int number = Convert.ToInt32(s);
return number;
}
catch
{
Console.WriteLine("输入有误,请重新输入");
s = Console.ReadLine();
}
}
}
public static void JudgeNumber(int n1,int n2)
{
if(n1<n2)
{
//符合题意
return;
}
else
{
Console.WriteLine("第一个数字不能大于或者等于第二个数字,请重新输入第一个数字:");
string s1 = Console.ReadLine();
//调用GetNumber
n1= GetNumber(s1);
Console.WriteLine("请重新输入第二个数字:");
string s2 = Console.ReadLine();
n2 = GetNumber(s2);
}
}
public static int GetSum(int n1,int n2)
{
int sum = 0;
for(int i=n1;i<=n2;i++)
{
sum += i;
}
return sum;
}
练习2:/*用方法实现:一个字符串数组:{"马龙"、"迈克尔乔丹"、"雷吉米勒"、"蒂姆邓肯"、"科比布莱恩特"}, 请输出最长的字符串。*/
static void Main(string[] args)
{
/*用方法实现:一个字符串数组:{"马龙"、"迈克尔乔丹"、"雷吉米勒"、"蒂姆邓肯"、"科比布莱恩特"};
请输出最长的字符串。*/
string[] names = { "马龙", "迈克尔乔丹", "雷吉米勒", "蒂姆邓肯", "科比布莱恩特" };
string max = GetLongest(names);
Console.WriteLine(max);
}
/// <summary>
/// 求一个字符串数组中最长的元素
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string GetLongest(string[] s)
{
string max = s[0];
for(int i=0;i<s.Length;i++)
{
if(s[i].Length>max.Length)
{
max = s[i];
}
}
return max;
}
练习3:用方法来实现:请计算出一个整型数组的平均值,保留两位小数。
static void Main(string[] args)
{
/*用方法来实现:请计算出一个整型数组的平均值,保留两位小数*/
int[] nums = { 1, 2, 7 };
double avg= GetAvg(nums);
//保留两位小数
//Console.WriteLine("{0:0.00}", avg);
string s = avg.ToString("0.00");
avg = Convert.ToDouble(s);
Console.WriteLine(avg);
}
public static double GetAvg(int[] nums)
{
double sum = 0;
for(int i=0;i<nums.Length;i++)
{
sum += nums[i];
}
return sum / nums.Length;
}
练习4:/*写一个方法,用来判断用户输入的数字是不是质数。
再写一个方法,要求用户只能输入数字,输入有误就一直让用户输入*/
static void Main(string[] args)
{
/*写一个方法,用来判断用户输入的数字是不是质数
再写一个方法,要求用户只能输入数字,输入有误就一直让用户输入*/
while (true)
{
Console.WriteLine("请输入一个数字,我们将判断你输入的数字是否是质数:");
string strNumber = Console.ReadLine();
int number = GetNumber(strNumber);
bool b = IsPrime(number);
Console.WriteLine(b);
}
}
/// <summary>
/// 必须输入数字
/// </summary>
/// <param name="strNumber"></param>
/// <returns></returns>
public static int GetNumber(string strNumber)
{
while(true)
{
try
{
int number = Convert.ToInt32(strNumber);
return number;
}
catch
{
Console.WriteLine("请重新输入:");
strNumber = Console.ReadLine();
}
}
}
/// <summary>
/// 判断数字是否是质数
/// </summary>
/// <param name="number"></param>
/// <returns></returns>
public static bool IsPrime(int number)
{
if(number<2)
{
return false;
}
else
{
//让这个数字从2开始除,除到自身的前一位
for(int i=2;i<number;i++)
{
if(number%i==0)
{
//不是质数
return false;
}
}
//质数
return true;
}
}
练习5:/*接受输入后判断其等级并显示出来。
判断依据如下:等级=优(90-100分),良(80-90分)*/
static void Main(string[] args)
{
/*接受输入后判断其等级并显示出来。
判断依据如下:等级=优(90-100分),良(80-90分)*/
Console.WriteLine("请输入考试成绩:");
int score = Convert.ToInt32(Console.ReadLine());
string level = GetLevel(score);
Console.WriteLine(level);
}
public static string GetLevel(int score)
{
string level = "";
switch(score/10)
{
case 10:
case 9: level = "优";break;
case 8: level = "良";break;
case 7: level = "中";break;
case 6: level = "差";break;
default: level = "不及格";break;
}
return level;
}
练习6:/*请将字符串数组{"中国","美国","巴西","澳大利亚","加拿大"} 中的内容反转*/
static void Main(string[] args)
{
/*请将字符串数组{"中国","美国","巴西","澳大利亚","加拿大"}
中的内容反转*/
string[] names = { "中国", "美国", "巴西", "澳大利亚", "加拿大" };
//Array.Reverse(names);
Test(names);
for(int i=0;i<names.Length;i++)
{
Console.WriteLine(names[i]);
}
}
/// <summary>
/// 反转数组
/// </summary>
/// <param name="names"></param>
public static void Test(string[] names)
{
for (int i = 0; i < names.Length / 2; i++)
{
string temp = names[i];
names[i] = names[names.Length - 1 - i];
names[names.Length - 1 - i] = temp;
}
}
练习7:/*写一个方法,计算圆的面积和周长,面积是PI*R*R,周长是2*PI*R */
static void Main(string[] args)
{
/*写一个方法,计算圆的面积和周长,面积是PI*R*R,周长是2*PI*R */
double r = 5;
double perimeter;
double area;
GetPerimeterArea(r, out perimeter, out area);
Console.WriteLine(perimeter);
Console.WriteLine(area);
}
public static void GetPerimeterArea(double r,out double perimeter, out double area)
{
perimeter = 2 * 3.14 * r;
area = 3.14 * r * r;
}
练习8:将一个字符串数组输出为|分割的形式,比如:“梅西|卡卡|郑大世”(用方法来实现此功能)
static void Main(string[] args)
{
//将一个字符串数组输出为|分割的形式,比如:“梅西|卡卡|郑大世”(用方法来实现此功能)
string[] names = { "梅西", "卡卡", "郑大世" };
string str = ProcessString(names);
Console.WriteLine(str);
}
public static string ProcessString(string[] names)
{
string str = null;
for (int i = 0; i < names.Length-1;i++)
{
str += names[i] + "|";
}
return str + names[names.Length - 1];
}
7. 飞行棋项目
1.画游戏头;
2.初始化地图(加载地图所需要的资源);
3.画地图;
4.玩游戏;
游戏规则:
如果玩家A踩到了玩家B,玩家B退6格;
踩到了地雷,退6格;
踩到了时空隧道,进10格;
踩到了幸运轮盘,1交换位置,2轰炸对方,使对方退6格;
踩到了暂停,暂停一回合;
踩到了方块,什么都不干;
using System;
class Program
{
//用静态字段来模拟全局变量
static int[] Maps = new int[100];
//声明一个静态数组用来存储玩家A和玩家B的坐标
static int[] PlayerPos = new int[2];
//存储两个玩家的姓名
static string[] PlayerNames = new string[2];
//两个玩家的标记
static bool[] Flags = new bool[2];
static void Main(string[] args)
{
GameShow();
#region 输入玩家姓名
Console.WriteLine("请输入玩家A的姓名:");
PlayerNames[0] = Console.ReadLine();
while(PlayerNames[0]=="")
{
Console.WriteLine("玩家A的姓名不能为空,请重新输入:");
PlayerNames[0] = Console.ReadLine();
}
Console.WriteLine("请输入玩家B的姓名:");
PlayerNames[1] = Console.ReadLine();
while(PlayerNames[1]==""||PlayerNames[1]==PlayerNames[0])
{
if(PlayerNames[1]=="")
{
Console.WriteLine("玩家B的姓名不能为空,请重新输入:");
PlayerNames[1] = Console.ReadLine();
}
else
{
Console.WriteLine("玩家B的姓名不能和玩家A的相同,请重新输入:");
PlayerNames[1] = Console.ReadLine();
}
}
#endregion
//玩家姓名输入完成之后,首先应该清屏
Console.Clear();//清屏
GameShow();
InitailMap();
DrawMap();
//当玩家A和玩家B都没有到达终点的时候,两个玩家就不停地玩
while(PlayerPos[0]<99&&PlayerPos[1]<99)
{
if (Flags[0] == false)
{
PlayGame(0);
}
else
{
Flags[0] = false;
}
if(PlayerPos[0]>=99)
{
Console.WriteLine("玩家{0}无耻的赢了玩家{1}", PlayerNames[0], PlayerNames[1]);
break;
}
if (Flags[1] == false)
{
PlayGame(1);
}
else
{
Flags[1] = false;
}
if (PlayerPos[1] >= 99)
{
Console.WriteLine("玩家{0}无耻的赢了玩家{1}", PlayerNames[1], PlayerNames[0]);
break;
}
}//while
GameOver();
}
/// <summary>
/// 游戏头
/// </summary>
public static void GameShow()
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("*********************************");
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("*********************************");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("*************飞行棋**************");
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("*********************************");
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("*********************************");
}
/// <summary>
/// 初始化地图
/// </summary>
public static void InitailMap()
{
int[] luckyturn = { 6, 23, 40, 55, 69, 83 };//幸运轮盘○
for(int i=0;i<luckyturn.Length;i++)
{
Maps[luckyturn[i]] = 1;
}
int[] landMine = { 5, 13, 17, 33, 38, 50, 64, 80, 94 };//地雷☆
for (int i = 0; i < landMine.Length; i++)
{
Maps[landMine[i]] = 2;
}
int[] pause = { 9, 27, 60,93 };//暂停△
for (int i = 0; i < pause.Length; i++)
{
Maps[pause[i]] = 3;
}
int[] timeTunnel = { 20, 25, 45, 63, 72, 88, 90 };//时空隧道卍
for (int i = 0; i < timeTunnel.Length; i++)
{
Maps[timeTunnel[i]] = 4;
}
}
/// <summary>
/// 画地图
/// </summary>
public static void DrawMap()
{
Console.WriteLine("图例:幸运轮盘:○ 地雷:☆ 暂停:△ 时空隧道:卍");
#region 第一横行
for (int i = 0; i < 30; i++)
{
Console.Write(DrawStringMap(i));
}//for
#endregion
//画完第一横行后,应该换行
Console.WriteLine();
#region 第一竖行
for(int i=30;i<35;i++)
{
for(int j=0;j<=28;j++)
{
Console.Write(" ");
}
Console.Write(DrawStringMap(i));
Console.WriteLine();
}
#endregion
#region 第二横行
for(int i=64;i>=35;i--)
{
Console.Write(DrawStringMap(i));
}
//画完第二横行 应该换行
Console.WriteLine();
#endregion
#region 第二竖行
for(int i=65;i<=69;i++)
{
Console.WriteLine(DrawStringMap(i));
}
#endregion
#region 第三横行
for(int i=70;i<=99;i++)
{
Console.Write(DrawStringMap(i));
}
#endregion
//画完最后一行应该换行
Console.WriteLine();
}
/// <summary>
/// 从DrawMap方法中抽象出来的一个方法
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public static string DrawStringMap(int i)
{
string str = "";
//如果玩家A和玩家B的坐标相同,并且都在地图上,则画一个尖括号
if (PlayerPos[0] == PlayerPos[1] && PlayerPos[0] == i)
{
str= "<>";
}
else if (PlayerPos[0] == i)
{
//注意将输入法切为全角后再输入A,一个全角占的空间=两个半角
str = "A";
}
else if (PlayerPos[1] == i)
{
//注意将输入法切为全角后再输入A,一个全角占的空间=两个半角
str = "B";
}
else
{
switch (Maps[i])
{
case 0:
Console.ForegroundColor = ConsoleColor.Yellow;
str = "□";
break;
case 1:
Console.ForegroundColor = ConsoleColor.Green;
str = "○";
break;
case 2:
Console.ForegroundColor = ConsoleColor.Red;
str = "☆";
break;
case 3:
Console.ForegroundColor = ConsoleColor.Blue;
str = "△";
break;
case 4:
Console.ForegroundColor = ConsoleColor.DarkCyan;
str = "卍";
break;
}//switch
}//else
return str;
}
/// <summary>
/// 玩游戏
/// </summary>
public static void PlayGame(int playerNumber)
{
Random r = new Random();
int rnumber = r.Next(1, 7);
Console.WriteLine("{0}按任意键开始掷骰子", PlayerNames[playerNumber]);
Console.ReadKey(true);
Console.WriteLine("{0}掷出了{1}", PlayerNames[playerNumber],rnumber);
PlayerPos[playerNumber] += rnumber;
ChangePos();
Console.ReadKey(true);
Console.WriteLine("{0}按任意键开始行动", PlayerNames[playerNumber]);
Console.ReadKey(true);
Console.WriteLine("{0}行动完了", PlayerNames[playerNumber]);
Console.ReadKey(true);
//玩家A有可能踩到了玩家B、方块、幸运轮盘、地雷、暂停、时空隧道
if (PlayerPos[playerNumber] == PlayerPos[1- playerNumber])
{
Console.WriteLine("玩家{0}踩到了玩家{1},玩家{2}退6格", PlayerNames[playerNumber], PlayerNames[1- playerNumber], PlayerNames[1- playerNumber]);
PlayerPos[1- playerNumber] -= 6;
ChangePos();
Console.ReadKey(true);
}
else//踩到了关卡
{
//玩家的坐标
switch (Maps[PlayerPos[playerNumber]])//0 1 2 3 4
{
case 0:
Console.WriteLine("玩家{0}踩到了方块,安全。", PlayerNames[playerNumber]);
Console.ReadKey(true);
break;
case 1:
Console.WriteLine("玩家{0}踩到了幸运轮盘,请选择:1--交换位置 2--轰炸对方", PlayerNames[playerNumber]);
string input = Console.ReadLine();
while (true)
{
if (input == "1")
{
Console.WriteLine("玩家{0}选择跟玩家{1}交换位置", PlayerNames[playerNumber], PlayerNames[1- playerNumber]);
Console.ReadKey(true);
int temp = PlayerPos[playerNumber];
PlayerPos[playerNumber] = PlayerPos[1- playerNumber];
PlayerPos[1- playerNumber] = temp;
Console.WriteLine("交换完成!!!按任意键继续游戏!!!");
Console.ReadKey(true);
break;
}
else if (input == "2")
{
Console.WriteLine("玩家{0}选择轰炸玩家{1},玩家{2}退6格", PlayerNames[playerNumber], PlayerNames[1- playerNumber], PlayerNames[1- playerNumber]);
Console.ReadKey(true);
PlayerPos[1- playerNumber] -= 6;
Console.WriteLine("玩家{0}退了6格", PlayerNames[1- playerNumber]);
Console.ReadKey(true);
break;
}
else
{
Console.WriteLine("只能输入1或2 1--交换位置 2--轰炸对方,请重新输入:");
input = Console.ReadLine();
}
}
break;
case 2:
Console.WriteLine("玩家{0}踩到了地雷,退6格", PlayerNames[playerNumber]);
Console.ReadKey(true);
PlayerPos[playerNumber] -= 6;
ChangePos();
break;
case 3:
Console.WriteLine("玩家{0}踩到了暂停,暂停一回合", PlayerNames[playerNumber]);
Flags[playerNumber] = true;
Console.ReadKey(true);
break;
case 4:
Console.WriteLine("玩家{0}踩到了时空隧道,前进10格", PlayerNames[playerNumber]);
PlayerPos[playerNumber] += 10;
ChangePos();
Console.ReadKey(true);
break;
}//switch
}//else
ChangePos();
Console.Clear();
DrawMap();
}
//限定玩家坐标范围在0-99之间
public static void ChangePos()
{
if(PlayerPos[0]<0)
{
PlayerPos[0] = 0;
}
if(PlayerPos[0]>=99)
{
PlayerPos[0] = 99;
}
if (PlayerPos[1] < 0)
{
PlayerPos[1] = 0;
}
if (PlayerPos[1] >= 99)
{
PlayerPos[1] = 99;
}
}
//游戏结束
public static void GameOver()
{
Console.WriteLine("***************游戏结束****************");
}
}
游戏截图:
八、面向对象初级
1. 类
面向过程:面向的是完成这件事儿的过程,强调的是完成这件事儿的动作。
比如:把大象塞进冰箱里;
1.打开冰箱门;
2.把大象塞进去,摸一下大象的耳朵;
3.关闭冰箱门;
张三(瘦小、低矮、小屌丝)
1.张三踩着小板凳打开冰箱门;
2.张三找到李四帮忙把大象塞进冰箱里,踩着板凳摸大象耳朵;
3.张三踩着板凳关闭冰箱门;
李四(高大威猛)
1.李四自己就能打开冰箱门;
2.李四自己将大象塞进冰箱里,站着就能摸大象耳朵;
3.李四自己关闭冰箱门;
由此可见,如果我们用面向过程的思想来解决这个事情,当执行这件事的人不同的时候,我们就需要为每个不同的人 量身定做解决事情的方法。
面向对象:找个对象帮你做事。
把大象塞进冰箱;
我们把冰箱作为对象:
1.冰箱门可以被打开;
2.大象可以被塞进冰箱里;
3.冰箱门可以被关闭;
张三:
张三做第1件事;
张三做第2件事;
张三做第3件事;
李四:
李四做第1件事;
李四做第2件事;
李四做第3件事;
面向对象:意在写出一个通用的代码,屏蔽差异。
面向过程:关门
张三:一脚把门揣紧了;
李四:轻轻把门关上了;
王五:胡乱的把门关上了,没关严实;
面向对象:关门
门可以被关上;
我们在代码中描述一个对象,通过描述这个对象的属性和方法;
对象必须是看得见摸得着的;
灯:属性和方法
属性:
外形:长的;
亮度:500W;
颜色:白色;
方法:
会发光;
我们把这些具有相同属性和相同方法的对象进行进一步的封装,抽象出来类这个概念。
类就是个模子,确定了对象应该具有的特征(属性)和行为(方法)。
类是对象的类型。对象是根据类创建出来的。
类就是一个盖大楼的图纸,对象就是盖出来的大楼。
类的语法:
[public] class 类名
{
字段;
属性;
方法;
}
写好了一个类之后,我们需要创建这个类的对象,
那么我们管创建这个类的对象的过程 称作:类的实例化。使用关键字:new。
this:表示当前这个类的对象。
类是不占内存的,而对象是占内存的。
public class Person
{
public string _name;
public int _age;
public char _gender;
public void CHLSS()
{
Console.WriteLine("我叫{0},我今年{1}岁了,我是{2}生,我可以吃喝拉撒睡。",this._name,this._age,this._gender);
}
}
class Program
{
static void Main(string[] args)
{
//创建Person类的对象
Person ZhangSan = new Person();
ZhangSan._name = "张三";
ZhangSan._age = 23;
ZhangSan._gender = '男';
ZhangSan.CHLSS();
Console.ReadKey();
}
}
2. 属性
属性的作用就是保护字段,对字段的赋值和取值进行限定。
属性的本质就是两个方法,一个叫get(),一个叫set();可读、可写;
Field——字段;Method——方法;Property——属性;
举个例子:字段是女人,属性是男人,属性存在的意义是为了保护女人不轻易被外界访问。
public class Person
{
private string _name;
public string Name
{
//当输出属性值的时候,会执行get方法
get { return _name; }
//当给属性赋值的时候,首先会执行set方法
set { _name = value; }
}
private int _age;
public int Age
{
get { return _age; }
set
{
if (value < 0 || value > 100)
{
value = 0;
}
_age = value;
}
}
private char _gender;
public char Gender
{
get
{
if (_gender != '男' && _gender != '女')
{
return _gender='男';
}
return _gender;
}
set { _gender = value; }
}
public void CHLSS()
{
Console.WriteLine("我叫{0},我今年{1}岁了,我是{2}生,我可以吃喝拉撒睡。",this.Name,this.Age,this.Gender);
}
}
class Program
{
static void Main(string[] args)
{
//创建Person类的对象
Person ZhangSan = new Person();
ZhangSan.Name = "张三";
ZhangSan.Age = 23;
ZhangSan.Gender = '男';
ZhangSan.CHLSS();
Console.ReadKey();
}
}
静态和非静态的区别
1.在非静态类中,既可以有实例成员,也可以有静态成员;
2.在调用实例成员的时候,需要使用对象名.实例成员;
在调用静态成员的时候,需要使用类名.静态成员名;
总结:
1.静态成员必须使用类名去调用,而实例成员使用对象名调用;
2.静态函数中,只能访问静态成员,不允许访问实例成员;
3.实例函数中,既可以使用静态成员,也可以使用实例成员;
4.静态类中,只允许有静态成员,不允许出现实例成员;
使用:
1.如果你想要你的类当做一个“工具类”去使用,这个时候可以考虑将类写成静态的;
2.静态类在整个项目中资源共享;
释放资源:GC(Garbage Collection垃圾回收器);
3. 构造函数
作用:帮助我们初始化对象(给对象的每个属性依次的赋值)。
构造函数是一个特殊的方法:
1.构造函数没有返回值,连void也不能写;
2.构造函数的名称必须跟类名一样;
3.构造函数是可以有重载的;(不同个数的参数)
创建对象的时候会执行构造函数。
类当中会有一个默认的无参数的构造函数,当你写一个新的构造函数之后,不管是有参还是无参,那个默认的无参构造函数都被干掉了。
class Student
{
public Student(string name, int age, char gender, int chinese, int math, int english)
{
this.Name = name;
this.Age = age;
this.Gender = gender;
this.Chinese = chinese;
this.Math = math;
this.English = english;
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private int _age;
public int Age
{
get { return _age; }
set { _age = value; }
}
private char _gender;
public char Gender
{
get { return _gender; }
set { _gender = value; }
}
private int _chinese;
public int Chinese
{
get { return _chinese; }
set { _chinese = value; }
}
private int _math;
public int Math
{
get { return _math; }
set { _math = value; }
}
private int _english;
public int English
{
get { return _english; }
set { _english = value; }
}
}
class Program
{
static void Main(string[] args)
{
Student zhangSan = new Student("张三", 18, '男', 100, 100, 100);
}
}
4. new关键字
new帮助我们做了三件事:
1.在内存中开辟了一块空间;
2.在开辟的空间中创建对象;
3.调用对象的构造函数进行初始化对象;
5. this 关键字
1.代表当前类的对象;
2.在类当中显示的调用本类的构造函数;
public Student(string name, int age, char gender, int chinese, int math, int english)
{
this.Name = name;
this.Age = age;
this.Gender = gender;
this.Chinese = chinese;
this.Math = math;
this.English = english;
}
public Student(string name, int age, char gender) : this(name,age,gender,0,0,0)
{
}
6. 析构函数
//当程序结束的时候,析构函数才执行
//帮助我们释放资源
//如果我们希望由GC自动释放资源,就不需要写析构函数
~Student()
{
Console.WriteLine("我是析构函数");
}
7. 索引器
以下是一个没有索引的简单类 的写法:
using System;
class Program
{
static void Main(string[] args)
{
Employee emp1 = new Employee();
emp1.LastName = "三";
emp1.FirstName = "张";
emp1.CityOfBirth = "郑州";
Console.WriteLine("名字:{0}", emp1.LastName);
Console.WriteLine("姓:{0}", emp1.FirstName);
Console.WriteLine("出生地:{0}", emp1.CityOfBirth);
}
}
//没有索引的简单类
class Employee
{
public string LastName;
public string FirstName;
public string CityOfBirth;
}
如果使用索引的话,如下图所示:
什么是索引器?
索引器是一组get和set访问器,与属性类似。表现形式如下:
索引器和属性的异同:
(1)和属性一样,索引器不用分配内存来存储;
(2)索引器和属性都主要被用来访问其他数据成员,它们与这些成员关联,并为它们提供获取和设置访问;
(3)属性通常表示单独的数据成员,索引器通常表示多个数据成员;
(4)和属性一样,索引器可以只有一个访问器,也可以两个都有;
(5)索引器总是实例成员,因此不能声明为static;
(6)和属性一样,实现get和set访问器的代码不必一定要关联到某个字段或属性。这段代码可以做任何事情 或什么也不做,只要get访问器返回某个指定类型的值即可。
总结:可以认为索引器是为类的多个数据成员提供get和set的属性。通过提供索引器,可以在许多可能的数据成员中进行选择。索引器本身可以是任何类型,不仅仅是数值类型。
声明索引器:
返回类型 this[参数列表]
{
get{ ...... }
set{ ....... }
}
注意:
(1)索引器没有名称,在名称的位置是关键字this;
(2)参数列表在方括号中间;
(3)参数列表中必须至少声明一个参数。
【例1】
using System;
class Program
{
static void Main(string[] args)
{
Employee emp1 = new Employee();
emp1[0] = "三";
emp1[1] = "张";
emp1[2] = "郑州";
Console.WriteLine("名字:{0}", emp1[0]);
Console.WriteLine("姓:{0}", emp1[1]);
Console.WriteLine("出生地:{0}", emp1[2]);
}
}
//添加索引器
class Employee
{
public string LastName;
public string FirstName;
public string CityOfBirth;
public string this[int index]
{
get
{
switch(index)
{
case 0: return LastName;
case 1: return FirstName;
case 2: return CityOfBirth;
default: throw new ArgumentOutOfRangeException("index");
}
}
set
{
switch(index)
{
case 0: LastName = value; break;
case 1: FirstName = value; break;
case 2: CityOfBirth = value; break;
default: throw new ArgumentOutOfRangeException("index");
}
}
}
}
【例2】
using System;
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
Console.WriteLine("temp0={0},temp1={1}", mc[0], mc[1]);
mc[0] = 15;
mc[1] = 20;
Console.WriteLine("temp0={0},temp1={1}", mc[0], mc[1]);
}
}
//添加索引器
class MyClass
{
int temp0;
int temp1;
public int this[int index]
{
get
{
return (index == 0) ? temp0 : temp1;
}
set
{
if (index == 0)
temp0 = value;
else
temp1 = value;
}
}
}
索引器重载:
只要索引器的参数列表不同,类就可以有任意多个索引器。索引器类型不同是不够的。这叫做索引器重载。因为所有的索引器都有相同的“名称”:this访问引用。
//索引器重载
class MyClass
{
string str;
int num;
public string this[int index]
{
get { return str; }
set { str = value; }
}
public string this[int index1,int index2]
{
get { return str; }
set { str = value; }
}
public int this[float index]
{
get { return num; }
set { num= value; }
}
}
总结:类中重载的索引器必须有不同的参数列表。
访问器(属性和索引器)的访问修饰符
访问器的访问修饰符有如下几个限制:
(1)仅当成员(属性或索引器)既有get访问器也有set访问器时,其访问器才能有访问修饰符;
(2)虽然两个访问器都必须出现,但它们中只能有一个有访问修饰符;
(3)访问器的访问修饰符必须比成员的访问级别有更严格的限制。
例如,如果一个属性的访问级别是public,那么可以把下图中的4个级别的任意一个级别给它的一个访问器。但如果属性的访问级别是protected,唯一能对访问器使用的访问修饰符是private。
using System;
class Program
{
static void Main(string[] args)
{
Person p = new Person("张三");
Console.WriteLine("这个人的名字叫:{0}", p.Name);
}
}
//访问器的访问修饰符
class Person
{
public string Name { get; private set; }//不同访问级别的访问器
public Person(string name)
{
Name = name;
}
}
练习题:
/*写一个Ticket类,有一个距离属性(本属性只读,在构造方法中赋值)
*不能为负数,有一个价格属性,价格属性只读,
*并且根据距离distance计算价格price(1元/公里)
*0-100公里 票价不打折;
*101-200公里 票价打9.5折;
*201-300公里 票价打9折;
*300公里以上 票价打8折;
*/
public class Ticket
{
private double _distance;
public double Distance
{
get { return _distance; }
}
public Ticket(double distance)
{
if(distance<0)
{
distance = 0;
}
this._distance = distance;
}
private double _price;
public double Price
{
get
{
if (_distance > 0 && _distance <= 100)
{
return _distance * 1.0;
}
else if (_distance >= 101 && _distance < 200)
{
return _distance * 0.95;
}
else if (_distance >= 201 && _distance <= 300)
{
return _distance * 0.9;
}
else
{
return _distance * 0.8;
}
}
}
public void ShowTicket()
{
Console.WriteLine("{0}公里需要{1}元", Distance, Price);
}
}
class Program
{
static void Main(string[] args)
{
Ticket t = new Ticket(90);
t.ShowTicket();
Console.ReadKey();
}
}
九、面向对象继承
命名空间namespace:
用于解决类重名问题,可以看做“类的文件夹”;
在一个项目中引用另一个项目中的类:
1.添加引用(在解决方案里添加引用);
2.引用命名空间;
值类型和引用类型
区别:
1.值类型和引用类型在内存上存储的地方不一样;
2.在传递值类型和引用类型的时候,传递的方式不一样,
值类型我们称之为值传递;引用类型我们称之为引用传递;
值类型:int、double、bool、char、decimal、struct、enum;
引用类型:string、class、数组;
值类型的值存储在内存的栈中;
引用类型的值存储在内存的堆中;
字符串的不可变性
当你给一个字符串重新赋值之后,老值并没有销毁,而是重新开辟一块空间存储新值。
当程序结束后,GC扫描整个内存,如果发现有的空间没有被指向,则立即把它销毁。
我们可以将字符串看做是char类型的一个只读数组。
既然可以将string看做char类型的只读数组,所以我可以通过下标去访问字符串中的某一个元素。
string s = "abcdefg";
//将字符串转换为char类型的数组
char[] chs = s.ToCharArray();
chs[0] = 'b';
//再将字符数组转换为我们的字符串
s = new string(chs);
字符串提供的各种方法
1.Length:获得当前字符串中字符的个数;
2.ToUpper:将字符转换成大写形式;
3.ToLower:将字符串转换成小写形式;
4.Equals(lessonTwo,StringComparison.OrdinalIgnoreCase):比较两个字符串,
5.Split():分割字符串,返回字符串类型的数组;
string s = "a1b2c3d4f5++--";
//分割字符串Split
char[] chs = { '1', '2', '3', '+', '-' };
string[] str = s.Split(chs, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < str.Length; i++)
{
Console.Write(str[i]);
}
6.Substring():解决字符串,在截取的时候包含要截取的位置;
7.Indexof():判断某个字符串在字符串中第一次出现的位置,如果没有则返回-1;
8.LastIndexof():判断某个字符串在字符串中最后一次出现的位置,如果没有则返回-1;
9.StartsWith():判断以。。。开始;
10.EndsWith():判断以。。。结束;
11.Replace():将字符串中某个字符串替换成一个新的字符串;
12.Contains():判断某个字符串是否包含指定的字符串;
13.Trim():去掉字符串中前后的空格;
14.TrimStart():去掉字符串中前面的空格;
15.TrimEnd():去掉字符串中结尾的空格;
16.string.IsNullOrEmpty():判断一个字符串是否为空或者为null;
17.string.Join():将数组按照指定的字符串连接,返回一个字符串。
继承
我们可能会在一些类中,写一些重复的成员,我们可以将这些重复的成员单独的封装到一个类中,作为这些类的父亲。
父类(基类):Person
子类(派生类):Student、Teacher、Driver
首先,子类继承了父类的属性和方法,但是子类并没有继承父类的私有字段。
问题:子类有没有继承父类的构造函数?
答:子类并没有继承父类的构造函数,但是子类会默认的调用父类无参的构造函数,创建父类对象,让子类可以使用父类中的成员。
所以,如果在父类中重新写了一个有参数的构造函数之后,那个无参数的构造函数就被干掉了,子类就调用不到了,所以子类会报错。
解决办法:
1.在父类中重新写一个无参数的构造函数;
2.在子类中显示的调用父类的构造函数,使用关键字:base();
继承的特性:
1.单根性:一个子类只能有一个父类;
2.传递性:
VS中"查看类图":可看清各个类之间的关系。
object是所有类的基类。
public class Person
{
public Person(string name, int age, char gender)
{
this.Name = name;
this.Age = age;
this.Gender = gender;
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private int _age;
public int Age
{
get { return _age; }
set { _age = value; }
}
private char _gender;
public char Gender
{
get { return _gender; }
set { _gender = value; }
}
public void CHLSS()
{
Console.WriteLine("吃喝拉撒睡");
}
}
public class Student : Person
{
public Student(string name, int age, char gender, int id) : base(name, age, gender)
{
this.Id = id;
}
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
public void Study()
{
Console.WriteLine("学生学习");
}
}
public class Teacher : Person
{
public Teacher(string name, int age, char gender, double salary) : base(name, age, gender)
{
this.Salary = salary;
}
private double _salary;
public double Salary
{
get { return _salary; }
set { _salary = value; }
}
public void Teach()
{
Console.WriteLine("老师教学");
}
}
public class Driver : Person
{
public Driver(string name, int age, char gender, int driveTime) : base(name, age, gender)
{
this.DriveTime = driveTime;
}
private int _driveTime;
public int DriveTime
{
get { return _driveTime; }
set { _driveTime = value; }
}
public void Drive()
{
Console.WriteLine("司机开车");
}
}
new关键字
作用:
1.创建对象;
2.隐藏从父类那里继承过来的同名成员,隐藏的后果就是子类调用不到父类的成员。
里氏转换
1.子类可以赋值给父类;(如果有一个地方需要一个父类作为参数,我们可以给一个子类代替)
2.如果父类中装的是子类对象,那么可以将这个父类强转为子类对象。
子类对象可以调用父类中的成员,但是父类对象永远都只能调用自己的成员。
is:表示类型转换;如果能够转换成功,则返回一个true,否则返回一个false;
as:表示类型转换;如果能够转换成功,则返回对应的对象,否则返回一个null;
public class Person
{
public void PersonSayHello()
{
Console.WriteLine("我是父类");
}
}
public class Student : Person
{
public void StudentSayHello()
{
Console.WriteLine("我是学生");
}
}
public class Teacher : Person
{
public void TeacherSayHello()
{
Console.WriteLine("我是老师");
}
}
class Program
{
static void Main(string[] args)
{
Person p = new Student();
//Student s = (Student)p;
//is的用法v
//if(p is Teacher)
//{
// Teacher t = (Teacher)p;
// t.TeacherSayHello();
//}
//else
//{
// Console.WriteLine("转换失败");
//}
//as的用法
Student s = p as Student;
s.StudentSayHello();
Console.ReadKey();
}
}
注意:我们将一个对象输出到控制台,默认情况下,打印的就是这个对象所在的类的命名空间
ArrayList集合
class Program
{
static void Main(string[] args)
{
//创建一个集合对象
ArrayList list = new ArrayList();
//集合:很多数据的一个集合
//数组:长度不可变、类型单一
//集合的好处:长度可以任意改变,类型随便
//添加单个元素如下:
list.Add(1);
list.Add(3.14);
list.Add(true);
list.Add("张三");
list.Add('男');
list.Add(5000m);
list.Add(new int[] { 1, 2, 3, 4 });
Person p = new Person();
list.Add(p);
list.Add(list);
//添加集合元素如下:
list.AddRange(new int[] { 1, 2, 3, 4, 5 });
list.AddRange(list);
list.Clear();//清空所有元素
list.Remove(true);//删除单个元素 写谁就删谁
list.RemoveAt(0);//根据下标去删除元素
list.RemoveRange(0, 3);//根据下标去移除一定范围的元素
list.Sort();//升序排列
list.Reverse();//反转
list.Insert(1, "插入的元素");//在指定的位置插入一个元素
list.InsertRange(0, new string[] { "张三", "李四" });//在指定的位置插入一个集合
list.Contains(1);//集合中是否包含该元素
for (int i = 0; i < list.Count; i++)
{
if (list[i] is Person)
{
((Person)list[i]).SayHello();
}
else if(list[i] is int[])
{
for(int j=0;j<((int[])list[i]).Length;i++)
{
Console.WriteLine(((int[])list[i])[j]);
}
}
else
{
Console.WriteLine(list[i]);
}
}
Console.ReadKey();
}
}
public class Person
{
public void SayHello()
{
Console.WriteLine("我是人类");
}
}
ArrayList集合长度问题
每次集合中实际包含的元素个数(count)超过了可以包含的元素个数(capcity)的时候,集合就会向内存中申请多开辟一倍的空间,来保证集合的长度一直够用。
class Program
{
static void Main(string[] args)
{
ArrayList list = new ArrayList();
list.Add(1);
Console.WriteLine(list.Count);
Console.WriteLine(list.Capacity);
//count 表示这个集合中实际包含的元素的个数
//capcity 表示这个集合中可以包含的元素个数
Console.ReadKey();
}
}
练习:
创建一个集合,里面添加一些数字,求平均值与和。
static void Main(string[] args)
{
ArrayList list = new ArrayList();
list.AddRange(new int[] { 1, 2, 3, 4, 5 });
int sum = 0;
for(int i=0;i<list.Count;i++)
{
sum += (int)list[i];
}
Console.WriteLine(sum);
Console.WriteLine(sum / 3);
Console.ReadKey();
}
写一个长度为10的集合,要求在里面随机地存放10个数字(0-9),但是要求所有的数字不重复。
class Program
{
static void Main(string[] args)
{
ArrayList list = new ArrayList();
Random r = new Random();
for(int i=0;i<10;i++)
{
int rNumber = r.Next(0, 10);
//集合中没有这个随机数
if(!list.Contains(rNumber))
{
list.Add(rNumber);
}
else//集合中有这个随机数
{
i--;//一旦产生了重复的随机数,这次循环就不算数
}
}
for(int i=0;i<list.Count;i++)
{
Console.WriteLine(list[i]);
}
Console.ReadKey();
}
}
HashTable 键值对集合
在键值对集合中,我们是根据键去找值的;
键值对对象[键]=值;
键值对集合当中,键必须是唯一的,而值是可以重复的。
foreach循环:用来遍历键值对。
var:根据值能够推断出来类型
C#是一门强类型语言:在代码当中,必须对每一个变量的类型有一个明确的定义;
如:int n=15;
js是一门弱语言,如:12 3.14 true "fdafda" 'c' var
class Program
{
static void Main(string[] args)
{
//创建了一个键值对集合对象
Hashtable ht = new Hashtable();
ht.Add(1, "张三");
ht.Add(2, true);
ht.Add(3, '男');
ht.Add(false, "错误");
ht.Add(5,"张三");
ht[6]="李四";//这也是一种添加数据的方式
ht[1]="把张三覆盖掉";
//在键值对集合中,是根据键去找值的
//Console.WriteLine(ht[1]);
//Console.WriteLine(ht[2]);
//Console.WriteLine(ht[3]);
//Console.WriteLine(ht[false]);
foreach(var item in ht.Keys)
{
Console.WriteLine(ht[item]);
}
Console.ReadKey();
}
}
Path
static void Main(string[] args)
{
string str = @"C:\3000soft\Red Spider\Data\Message\老赵.wav";
//获得文件名
Console.WriteLine(Path.GetFileName(str));
//获得文件名但是不包括扩展名
Console.WriteLine(Path.GetFileNameWithoutExtension(str));
//获得文件的扩展名
Console.WriteLine(Path.GetExtension(str));
//获得文件所在的文件夹的名称
Console.WriteLine(Path.GetDirectoryName(str));
//获得文件所在的全路径
Console.WriteLine(Path.GetFullPath(str));
//连接两个字符串作为路径
Console.WriteLine(Path.Combine(@"C:\a\", "b.txt"));
Console.ReadKey();
}
File
static void Main(string[] args)
{
//创建一个文件
//File.Create(@"G:\测试_备用\file学习.txt");
//删除一个文件
//File.Delete(@"G:\测试_备用\file学习.txt");
//复制一个文件
//File.Copy(@"G:\测试_备用\复制.txt", @"G:\测试_备用\粘贴.txt");
//读取文件
//byte[] buffer = File.ReadAllBytes(@"G:\测试_备用\file学习.txt");
//string s = Encoding.Default.GetString(buffer);
//写入文件(没有这个文件的话,会给你创建一个,有的话,会给你覆盖掉)
//string str = "今天天气好晴朗,处处好风光";
//byte[] buffer = Encoding.Default.GetBytes(str);
//File.WriteAllBytes(@"G:\测试_备用\file学习.txt", buffer);
//逐行读取(返回值为数组)
string[] contents = File.ReadAllLines(@"G:\测试_备用\file学习.txt", Encoding.Default);
foreach(string item in contents)
{
Console.WriteLine(item);
}
//逐行写入
File.WriteAllLines(@"G:\测试_备用\file学习.txt", new string[] { "abc", "def" });
//读取文本(返回值为字符串)
string str = File.ReadAllText(@"G:\测试_备用\file学习.txt", Encoding.Default);
Console.WriteLine(str);
Console.ReadKey();
//写入文本
File.WriteAllText(@"G:\测试_备用\file学习.txt", "abcdef");
File.AppendAllText(@"G:\测试_备用\file学习.txt", "增加字符串,不会覆盖");
}
编码:
将字符串以怎样的形式保存为二进制。
乱码
产生乱码的原因:就是你保存这个文件采用的编码,跟你打开这个文件所采用的编码格式不一样。
绝对路径和相对路径
绝对路径:通过给定的这个路径直接能在我的电脑中找到这个文件;
相对路径:文件相对于应用程序的路径;
我们在开发中应该去尽量的使用相对路径。
泛型集合
static void Main(string[] args)
{
//创建泛型集合对象
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
list.AddRange(new int[] { 1, 2, 3, 4, 5, 6 });
list.AddRange(list);
//List泛型集合可以转换为数组
int[] nums= list.ToArray();
List<int> listTwo= nums.ToList();
}
装箱和拆箱
装箱:将值类型转换为引用类型;
拆箱:将引用类型转换为值类型;
看两种类型是否发生了装箱和拆箱,要看,这两种类型是否存在继承关系。
static void Main(string[] args)
{
int n = 10;
object o = n;//装箱
int n2 = (int)o;//拆箱
//ArrayList list = new ArrayList(); //用时00:00:06.3582923
List<int> list = new List<int>();//用时00:00:00.5262316
//这个循环使用ArrayList发生了10000万次装箱操作
Stopwatch sw = new Stopwatch();
sw.Start();
for(int i=0;i<100000000;i++)
{
list.Add(i);
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
Console.ReadKey();
//这个地方没有发生任何的装箱和拆箱
string str = "123";
int i = Convert.ToInt32(str);
int a = 10;
IComparable b = a;//装箱
}
Dictionary字典
static void Main(string[] args)
{
Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(1, "张三");
dic.Add(2, "李四");
dic.Add(3, "王五");
dic[1] = "让张三滚蛋";
foreach(var item in dic.Keys)
{
Console.WriteLine("{0}----{1}", item, dic[item]);
}
foreach(KeyValuePair<int,string> kv in dic)
{
Console.WriteLine("{0}----{1}", kv.Key, kv.Value);
}
Console.ReadKey();
}
练习
/*将一个数组中的奇数放到一个集合中,再将偶数放到另一个集合中
* 最终将两个集合合并为一个集合,并且奇数显示在左边,偶数显示在右边。
*/
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
List<int> listOu = new List<int>();
List<int> listJi = new List<int>();
for(int i=0;i<nums.Length;i++)
{
if(nums[i]%2==0)
{
listOu.Add(nums[i]);
}
else
{
listJi.Add(nums[i]);
}
}
listJi.AddRange(listOu);
foreach(int item in listJi)
{
Console.Write(item + " ");
}
/*提示用户输入一个字符串,通过foreach循环将用户输入的字符串赋值给一个字符数组。
*/
Console.WriteLine("请输入一个字符串:");
string input = Console.ReadLine();
char[] chs = new char[input.Length];
int i = 0;
foreach(var item in input)
{
chs[i] = item;
i++;
}
foreach(var item in chs)
{
Console.WriteLine(item);
}
/*统计Welcome to china中每个字符出现的次数,不考虑大小写。
*/
string str = "Welcome to China";
//字符------出现次数
//键--------值
Dictionary<char, int> dic = new Dictionary<char, int>();
for(int i=0;i<str.Length;i++)
{
if(str[i]==' ')
{
continue;//不要空格的统计
}
//如果dic已经包含了当前循环到的这个键
if(dic.ContainsKey(str[i]))
{
//值再次加1
dic[str[i]]++;
}
else//这个字符在集合中是第一次出现
{
dic[str[i]] = 1;
}
}
foreach(KeyValuePair<char,int> kv in dic)
{
Console.WriteLine("字母{0}出现了{1}次", kv.Key, kv.Value);
}
FileStream文件流
将创建文件流对象的过程写在using当中,会自动地帮助我们释放流所占用的资源
class Program
{
static void Main(string[] args)
{
//FileStream 用来操作字节的
//StreamReader和StreamWriter 用来操作字符的
/*使用FileStream来读取数据*/
//1.创建FileStream对象
FileStream fsRead = new FileStream(@"G:\测试_备用\new.txt", FileMode.OpenOrCreate, FileAccess.Read);
byte[] buffer1 = new byte[1024 * 1024 * 5];
fsRead.Read(buffer1, 0, buffer1.Length);
//返回本次实际读取到的有效字节数
int r = fsRead.Read(buffer1, 0, buffer1.Length);
//将字节数组中每一个元素按照指定的编码格式解码成字符串
string s = Encoding.Default.GetString(buffer1, 0, r);
//关闭流
fsRead.Close();
//释放流所占用的资源
fsRead.Dispose();
Console.WriteLine(s);
//使用FileStream来写入数据
using (FileStream fsWrite = new FileStream(@"G:\测试_备用\new.txt", FileMode.OpenOrCreate, FileAccess.Write))
{
string str = "看我把你覆盖掉";
byte[] buffer2 = Encoding.UTF8.GetBytes(str);
fsWrite.Write(buffer2, 0, buffer2.Length);
}
Console.ReadKey();
}
}
使用文件流来实现多媒体文件的复制
class Program
{
static void Main(string[] args)
{
//思路:就是先将要复制的多媒体文件读取出来,然后再写入到你指定的位置。
string source = @"G:\测试_备用\复制.mp3";
string target = @"G:\测试_备用\粘贴.mp3";
CopyFile(source, target);
Console.ReadKey();
}
public static void CopyFile(string source, string target)
{
//我们创建一个负责读取的流
using (FileStream fsRead = new FileStream(source, FileMode.Open, FileAccess.Read))
{
//创建一个负责写入的流
using (FileStream fsWrite = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Write))
{
byte[] buffer = new byte[1024 * 1024 * 5];
//因为文件可能会比较大,所以我们在读取的时候,应该通过一个循环去读取
while(true)
{
//返回本次读取实际读取到的字节数
int r = fsRead.Read(buffer, 0, buffer.Length);
//如果返回一个0,也就意味着什么都没有读取到,读取完了
if(r==0)
{
break;
}
fsWrite.Write(buffer, 0, r);
}
}
}
}
}
StreamReader和SteamWriter
static void Main(string[] args)
{
//使用StreamReader来读取一个文本文件
using (StreamReader sr = new StreamReader(@"G:\测试_备用\new.txt", Encoding.Default))
{
while (!sr.EndOfStream)
{
Console.WriteLine(sr.ReadLine());
}
}
//使用StreamWriter来写入一个文本文件
using (StreamWriter sw = new StreamWriter(@"G:\测试_备用\new.txt"))
{
sw.Write("今天天气好晴朗");
}
Console.ReadKey();
}
十、面向对象多态
概念:让一个对象能够表现出多种的状态(类型)
实现多态的三种方法:虚方法virtual、抽象类abstract、接口Interface。
1.虚方法virtual与覆写方法override
首先,在这里我们 使用基类(父类)的引用:
using System;
class Program
{
static void Main(string[] args)
{
//使用父类的引用
MyDerivedClass derived = new MyDerivedClass();//创建一个子类对象
MyBaseClass based = (MyBaseClass)derived;//转换为父类
derived.Print();//从子类中调用Print
based.Print();//从父类中调用Print
}
}
//基类(父类)
class MyBaseClass
{
public void Print()
{
Console.WriteLine("这是一个父类");
}
}
//派生类(子类)
class MyDerivedClass:MyBaseClass
{
new public void Print()
{
Console.WriteLine("这是一个子类");
}
}
由上可知:当使用基类(父类)引用访问派生类(子类)对象时,得到的是基类(父类)的成员。而虚方法则可以让基类(父类)的引用访问“伸手到”派生类(子类)中取。
使用父类引用调取子类的方法需要满足以下条件:
(1)子类的方法和父类的方法有相同的签名和返回类型;
(2)父类的方法使用virtual标注;
(3)子类的方法使用override标注。
上面的代码可修改为:
using System;
class Program
{
static void Main(string[] args)
{
//使用父类的引用
MyDerivedClass derived = new MyDerivedClass();//创建一个子类对象
MyBaseClass based = (MyBaseClass)derived;//转换为父类
derived.Print();//从子类中调用Print
based.Print();//从父类中调用Print
}
}
//基类(父类)
class MyBaseClass
{
virtual public void Print()
{
Console.WriteLine("这是一个父类");
}
}
//派生类(子类)
class MyDerivedClass:MyBaseClass
{
override public void Print()
{
Console.WriteLine("这是一个子类");
}
}
注意事项:
(1)virtual和override的方法必须有相同的可访问性。即:要是public,则都应该是public;
(2)不能覆写 静态(static)方法或非虚(不含virtual)方法;
(3)方法、属性、索引器、事件,这些都可以被声明为virtual和override。
接下来是override孙类(使用override声明Print方法):
using System;
class Program
{
static void Main(string[] args)
{
//使用父类的引用
MySecondDerived secondDerived = new MySecondDerived();//创建一个孙类对象
MyBaseClass based = (MyBaseClass)secondDerived;//转换为父类
secondDerived.Print();//从孙类中调用Print
based.Print();//从父类中调用Print
}
}
//基类(父类)
class MyBaseClass
{
virtual public void Print()
{
Console.WriteLine("这是一个父类");
}
}
//派生类(子类)
class MyDerivedClass:MyBaseClass
{
override public void Print()
{
Console.WriteLine("这是一个子类");
}
}
//二级派生类(孙类)
class MySecondDerived:MyDerivedClass
{
override public void Print()
{
Console.WriteLine("这是一个孙类");
}
}
由上可知:不管Print方法是通过多少级子类调用,还是通过父类调用,最终都会调用辈分最小最低的子类中的方法。
接下来是new孙类(使用new声明Print方法):
using System;
class Program
{
static void Main(string[] args)
{
//使用父类的引用
MySecondDerived secondDerived = new MySecondDerived();//创建一个孙类对象
MyBaseClass based = (MyBaseClass)secondDerived;//转换为父类
secondDerived.Print();//从孙类中调用Print
based.Print();//从父类中调用Print
}
}
//基类(父类)
class MyBaseClass
{
virtual public void Print()
{
Console.WriteLine("这是一个父类");
}
}
//派生类(子类)
class MyDerivedClass:MyBaseClass
{
override public void Print()
{
Console.WriteLine("这是一个子类");
}
}
//二级派生类(孙类)
class MySecondDerived:MyDerivedClass
{
new public void Print()
{
Console.WriteLine("这是一个孙类");
}
}
由上可知:当孙类被标记为new后,其Print方法被父类引用调用时,方法调用会向上传递一级,即调用子类中的Print方法。
覆写属性如下:
using System;
class Program
{
static void Main(string[] args)
{
//使用父类的引用
MyDerivedClass derived = new MyDerivedClass();//创建一个子类对象
MyBaseClass based = (MyBaseClass)derived;//转换为父类
Console.WriteLine("子类中的属性_myInt值为:{0}", derived.MyProperty);
Console.WriteLine("父类中的属性_myInt值为:{0}", based.MyProperty);
}
}
//基类(父类)
class MyBaseClass
{
//属性
private int _myInt = 5;
virtual public int MyProperty
{
get { return _myInt; }
}
}
//派生类(子类)
class MyDerivedClass:MyBaseClass
{
//属性
private int _myInt = 10;
override public int MyProperty
{
get { return _myInt; }
}
}
【例1】
class Program
{
static void Main(string[] args)
{
Chinese cn1 = new Chinese("李雷");
Chinese cn2 = new Chinese("韩梅梅");
Japanese j1 = new Japanese("本田");
Japanese j2 = new Japanese("丰田");
Korea k1 = new Korea("三星");
Korea k2 = new Korea("泡菜");
Person[] pers = { cn1, cn2, j1, j2, k1, k2 };
for(int i=0;i<pers.Length;i++)
{
// if (pers[i] is Chinese)
// {
// ((Chinese)pers[i]).SayHello();
// }
// else if(pers[i]is Japanese)
// {
// ((Japanese)pers[i]).SayHello();
// }
// else if (pers[i] is Korea)
// {
// ((Korea)pers[i]).SayHello();
// }
pers[i].SayHello();//代替上面一坨代码
}
Console.ReadKey();
}
}
public class Person
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
public Person(string name)
{
this.Name = name;
}
public virtual void SayHello()
{
Console.WriteLine("我是人类");
}
}
public class Chinese:Person
{
public Chinese(string name):base(name)
{
}
public override void SayHello()
{
Console.WriteLine("我是中国人,我叫{0}", this.Name);
}
}
public class Japanese : Person
{
public Japanese(string name) : base(name)
{
}
public override void SayHello()
{
Console.WriteLine("我是日本人,我叫{0}", this.Name);
}
}
public class Korea : Person
{
public Korea(string name) : base(name)
{
}
public override void SayHello()
{
Console.WriteLine("我是朝鲜人,我叫{0}", this.Name);
}
}
【例2】
class Program
{
static void Main(string[] args)
{
//真的鸭子嘎嘎叫,木头鸭子吱吱叫,橡皮鸭子叽叽叫
RealDuck rd = new RealDuck();
MTDuck md = new MTDuck();
XPDuck xd = new XPDuck();
RealDuck[] ducks = { rd, md, xd };
for(int i=0;i<ducks.Length;i++)
{
ducks[i].Bark();
}
Console.ReadKey();
}
}
public class RealDuck
{
public virtual void Bark()
{
Console.WriteLine("真的鸭子嘎嘎叫");
}
}
public class MTDuck:RealDuck
{
public override void Bark()
{
Console.WriteLine("木头鸭子吱吱叫");
}
}
public class XPDuck:RealDuck
{
public override void Bark()
{
Console.WriteLine("橡皮鸭子叽叽叫");
}
}
2.抽象类
抽象成员是指设计为被覆写的函数成员。
一共有4个类型的成员可以声明为抽象的:方法、属性、事件、索引。
//抽象类
abstract class AbstractClass
{
//抽象方法
abstract public void PrintStuff(string s);
//抽象属性
abstract public int MyProperty
{
get;
set;
}
}
抽象成员有以下特征:
(1)抽象成员必须标记为abstract,并且不能有任何实现;
(2)抽象成员必须在抽象类中;
(3)抽象类不能被实例化;
(4)子类继承抽象类后,必须把父类中的所有抽象成员都override重写;(除非子类也是一个抽象类,才可以不重写)
(5)抽象成员的访问修饰符不能是private;
(6)在抽象类中可以包含实例成员;并且抽象类的实例成员可以不被子类实现;
(7)抽象类是有构造函数的,虽然不能被实例化;
(8)如果父类的抽象方法中有参数,那么,继承这个抽象父类的子类在重写父类的方法的时候必须传入对应的参数;
(9)如果父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候,也必须要传入返回值;
(10)如果父类中的方法有默认的实现,并且父类需要被实例化,这时可以考虑将父类定义成一个普通类,用虚方法来实现多态;
(11)如果父类中的方法没有默认实现,父类也不需要被实例化,则可以将该类定义为抽象类。
抽象类就是指设计被被继承的类。抽象类只能被用作其他类的基类。
abstract class MyClass
{ }
抽象类可以派生自另一个抽象类。如:
abstract class FirstAbstractClass
{
}
abstract class SecondAbstractClass:FirstAbstractClass
{
}
【例1】:抽象类和抽象方法
using System;
class Program
{
static void Main(string[] args)
{
//BaseAbstractClass based = new BaseAbstractClass();//错误提示:抽象类不能实例化
DerivedAbstractClass derived = new DerivedAbstractClass();//实例化子类
derived.InstanceMethod();//调用继承自父类中的实例方法
derived.AbstractMethod();//调用抽象方法
}
}
//抽象父类
abstract class BaseAbstractClass
{
//实例方法
public void InstanceMethod()
{
Console.WriteLine("实例方法");
}
//抽象方法
abstract public void AbstractMethod();
}
//抽象子类
class DerivedAbstractClass:BaseAbstractClass
{
//抽象方法的实现
public override void AbstractMethod()
{
Console.WriteLine("抽象方法");
}
}
【例2】
using System;
class Program
{
static void Main(string[] args)
{
DerivedAbstractClass derived = new DerivedAbstractClass();
derived.Print("这是一个字符串");
derived.MyInt = 66;
Console.WriteLine(derived.MyInt);
Console.WriteLine("面积为:{0}", derived.Area());
}
}
//抽象父类
abstract class BaseAbstractClass
{
public int length = 10;//数据成员
const int width = 3;//数据成员
abstract public void Print(string s);//抽象方法
abstract public int MyInt { get; set; }//抽象属性
//实例方法
public int Area()
{
return length * width;
}
}
//抽象子类
class DerivedAbstractClass:BaseAbstractClass
{
//覆盖抽象方法
public override void Print(string s)
{
Console.WriteLine(s);
}
//覆盖抽象属性
private int _myInt;
public override int MyInt
{
get { return _myInt; }
set { _myInt = value; }
}
}
【例3】
class Program
{
static void Main(string[] args)
{
//狗会叫,猫也会叫
Animal d = new Dog();
d.Bark();
Animal c = new Cat();
c.Bark();
Console.ReadKey();
}
}
public abstract class Animal
{
public abstract void Bark();
}
public class Dog : Animal
{
public override void Bark()
{
Console.WriteLine("旺旺");
}
}
public class Cat : Animal
{
public override void Bark()
{
Console.WriteLine("喵喵");
}
}
【例4】
using System;
namespace Test_CSharp
{
class Program
{
static void Main(string[] args)
{
//使用多态求矩形的面积和周长以及圆形的面积和周长
Shape shape = new Square(5, 6); //new Circle(5);
double area = shape.GetArea();
double perimeter = shape.GetPerimeter();
Console.WriteLine("这个形状的面积为:{0},周长是:{1}", area, perimeter);
Console.ReadKey();
}
}
public abstract class Shape
{
public abstract double GetArea();
public abstract double GetPerimeter();
}
public class Circle:Shape
{
private double _r;
public double R
{
get { return _r; }
set { _r = value; }
}
public Circle(double r)
{
this.R = r;
}
public override double GetArea()
{
return Math.PI * this.R * this.R;
}
public override double GetPerimeter()
{
return 2 * Math.PI * this.R;
}
}
public class Square:Shape
{
private double _length;
public double Length
{
get { return _length; }
set { _length = value; }
}
private double _width;
public double Width
{
get { return _length; }
set { _length = value; }
}
public Square(double length,double width)
{
this.Length = length;
this.Width = width;
}
public override double GetArea()
{
return this.Length * this.Width;
}
public override double GetPerimeter()
{
return 2 * (this.Length + this.Width);
}
}
}
【例5】
using System;
namespace Test_CSharp
{
class Program
{
static void Main(string[] args)
{
//用多态来实现将移动硬盘或者U盘或者MP3插到电脑上进行读写数据
MobileDisk md = new MobileDisk();
UDisk u = new UDisk();
MP3Disk mp3 = new MP3Disk();
Computer cpu = new Computer();
cpu.CpuRead(mp3);
cpu.CpuWrite(mp3);
Console.ReadKey();
}
}
/// <summary>
/// 抽象父类
/// </summary>
public abstract class MobileStorage
{
public abstract void Read();
public abstract void Write();
}
/// <summary>
/// 移动硬盘类
/// </summary>
public class MobileDisk : MobileStorage
{
public override void Read()
{
Console.WriteLine("移动硬盘在读取数据");
}
public override void Write()
{
Console.WriteLine("移动硬盘在写入数据");
}
}
/// <summary>
/// U盘类
/// </summary>
public class UDisk : MobileStorage
{
public override void Read()
{
Console.WriteLine("U盘在读取数据");
}
public override void Write()
{
Console.WriteLine("U盘在写入数据");
}
}/// <summary>
/// MP3类
/// </summary>
public class MP3Disk : MobileStorage
{
public override void Read()
{
Console.WriteLine("MP3在读取数据");
}
public override void Write()
{
Console.WriteLine("MP3在写入数据");
}
public void PlayMusic()
{
Console.WriteLine("MP3在播放音乐");
}
}
public class Computer
{
public void CpuRead(MobileStorage ms)
{
ms.Read();
}
public void CpuWrite(MobileStorage ms)
{
ms.Write();
}
}
}
3.访问修饰符
五个访问级别:
(1)public:公开的公共的;
public访问级别是限制性最少的。所有的类,包括程序集内部的类 和 外部的类都可以自由地访问成员。
(2)private:私有的,只能在当前类的内部访问;
private访问级别是限制最严格的,只能被它自己的类的成员访问。它不能被其他的类访问,包括继承它的类。
(3)protected:受保护的,只能在当前类的内部以及该类的子类中访问;
protected受保护成员允许派生自该类的类 访问该成员。注意:即使程序集外部继承该类的类 也能访问该成员。
(4)internal:只能在当前项目中访问,在同一个项目中,internal和public的权限是一样;
标记为internal的成员 对程序集内部的所有类可见,但是对程序集外部的类不可见。
(5)protected internal
标记为protected internal的成员对所有继承该类的类以及所有程序集内部的类可见。
注意事项:
(1)能够修饰类的访问修饰符只有两个:public、internal;
(2)子类的访问权限不能高于父类的访问权限,会暴露父类的成员;
(3)必须对每个成员指定成员的访问级别(如果不指定某个成员的访问级别,那它的隐式访问级别就默认为private);
(4)成员不能比它的类有更高的可访问性。也就是说:如果一个类的可访问性 限于它所在的程序集,那么类的成员个体 也不能从程序集的外部看到,无论它们的访问修饰符是什么,public也不例外。
4.设计模式
概念:设计这个项目的一种方式。
简单工厂模式
using System;
namespace Test_CSharp
{
public abstract class NoteBook
{
public abstract void SayHello();
}
public class Lenovo : NoteBook
{
public override void SayHello()
{
Console.WriteLine("我是联想笔记本");
}
}
public class Acer : NoteBook
{
public override void SayHello()
{
Console.WriteLine("我是宏基笔记本");
}
}
public class Dell : NoteBook
{
public override void SayHello()
{
Console.WriteLine("我是戴尔笔记本");
}
}
public class IBM : NoteBook
{
public override void SayHello()
{
Console.WriteLine("我是IBM笔记本");
}
}
class Program
{
//简单工厂的核心:根据用户的输入创建对象赋值给父类
public static NoteBook GetNoteBook(string brand)
{
NoteBook nb = null;
switch (brand)
{
case "Lenovo":
nb = new Lenovo();
break;
case "Acer":
nb = new Acer();
break;
case "Dell":
nb = new Dell();
break;
case "IBM":
nb = new IBM();
break;
}
return nb;
}
static void Main(string[] args)
{
Console.WriteLine("请输入您想要的笔记本品牌:");
string brand = Console.ReadLine();
NoteBook nb = GetNoteBook(brand);
nb.SayHello();
Console.ReadKey();
}
}
}
5.序列化与反序列化
序列化:就是将对象转换为二进制
反序列化:就是将二进制转换为对象
作用:传输数据。将这个类标记为可以被序列化的。
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Test_CSharp
{
[Serializable]
public class Person
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private char _gender;
public char Gender
{
get { return _gender; }
set { _gender = value; }
}
private int _age;
public int Age
{
get { return _age; }
set { _age = value; }
}
}
class Program
{
static void Main(string[] args)
{
//要将p这个对象 序列化为二进制 传输给对方电脑
//Person p = new Person();
//p.Name = "张三";
//p.Age = 19;
//p.Gender = '男';
//using (FileStream fsWrite=new FileStream(@"G:\测试_备用\new.txt",FileMode.OpenOrCreate,FileAccess.Write))
//{
// //开始序列化对象
// BinaryFormatter bf = new BinaryFormatter();
// bf.Serialize(fsWrite, p);
//}
//Console.WriteLine("序列化成功");
//接手对方发送过来的二进制 反序列化成对象
Person p;
using (FileStream fsRead=new FileStream(@"G:\测试_备用\new.txt", FileMode.OpenOrCreate, FileAccess.Read))
{
BinaryFormatter bf = new BinaryFormatter();
p = (Person)bf.Deserialize(fsRead);
}
Console.WriteLine(p.Name);
Console.WriteLine(p.Age);
Console.WriteLine(p.Gender);
Console.ReadKey();
}
}
}
6.partial部分类
类的声明可以分割成几个部分类的声明。
//部分类
partial class MyPartClass
{
//member1 declaration
//member2 declaration
}
//部分类
partial class MyPartClass
{
//member3 declaration
//member4 declaration
}
注意:
(1)每个部分类的声明都含有一些类成员的声明;
(2)类的部分类声明可以在同一文件中,也可以在不同文件中;
(3)每个局部声明必须被标记为partial class,而不是单独的关键字class。部分类声明看起来和普通类声明相同,除了附加的修饰符partial;
(4)组成类的所有分部类声明必须在一起编译。使用分部类声明的类必须有相同的含义,就好像所有类成员都声明在一个单独的类声明体内。
部分方法是声明在部分类中不同部分的方法。部分方法的不同部分可以声明在不同的部分类中,也可以声明在同一个类中。
部分方法的两个部分如下:
(1)定义部分方法声明:
(a)给出签名和返回类型;
(b)声明的实现部分只是一个分号。
(2)实现部分方法声明:
(a)给出签名和返回类型;
(b)是以正常形式的语句块实现。
using System;
class Program
{
static void Main(string[] args)
{
var mc = new MyPartClass();
mc.Add(5, 6);
}
}
//部分类
partial class MyPartClass
{
partial void PrintSum(int x, int y);//定义部分方法,没有实现部分
public void Add(int x,int y)
{
PrintSum(x, y);
}
}
//部分类
partial class MyPartClass
{
partial void PrintSum(int x, int y)//实现部分方法
{
Console.WriteLine("总和为:{0}", x + y);
}
}
【例1】
namespace Test_CSharp
{
public partial class Person
{
private string _name;
public void Test()
{ }
}
public partial class Person
{
public void Test(string name)
{ }
}
class Program
{
static void Main(string[] args)
{
}
}
}
7.sealed密封类
不能够被其他类继承,但是可以继承于其他类。
注意:
(1)密封类只能被用作独立的类,不能被用作基类(父类);
(2)密封类使用sealed修饰符标注。
public sealed class Person:Test
{
}
public class Test
{
}
8.重写ToString()方法
using System;
namespace Test_CSharp
{
class Program
{
static void Main(string[] args)
{
Person p = new Person();
Console.WriteLine(p.ToString());
Console.ReadKey();
}
}
public class Person
{
public override string ToString()
{
return "Hello World";
}
}
}
9.接口
接口是指定一组函数成员 而不实现它们的引用类型。所以只能类和结构来实现接口。
接口是一种规范。
只要一个类继承了一个接口,这个类就必须实现这个接口中所有的成员。
接口不能被实例化,也就是说,接口不能new(不能创建对象)。
接口中的成员不能加“访问修饰符”,接口中的成员访问修饰符为public,不能修改。
(默认为public)
接口中的成员不能有任何实现(“光说不做”,只是定义了一组未实现的成员)。
接口中只能有方法、属性、索引器、事件,不能有字段和构造函数。
接口与接口之间可以继承,并且可以多继承。
接口并不能去继承一个类,而类可以继承接口 (接口只能继承于接口,而类既可以继承接口,也可以继承类)
实现接口的子类必须实现该接口的全部成员。
一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承了父类A,并实现了接口IA,那么语法上A必须写在IA的前面。
class MyClass:A,IA{},因为类是单继承的。
显式实现接口的目的:解决方法的重名问题。
什么时候显式的去实现接口:当继承的接口中的方法和参数一模一样的时候,要是用显式的实现接口。
当一个抽象类实现接口的时候,需要子类去实现接口。
【例1】
using System;
class Program
{
//传入接口的引用
static void PrintInfo(IInfo item)
{
Console.WriteLine("姓名:{0},年龄:{1}", item.GetName(), item.GetAge());
}
static void Main(string[] args)
{
Teacher teacher = new Teacher() { Name = "张三", Age = 35 };
Student student = new Student() { FirstName = "李", LastName = "四", PersonsAge = 14 };
PrintInfo(teacher);
PrintInfo(student);
}
}
//声明接口
interface IInfo
{
string GetName();
string GetAge();
}
//声明实现了接口的教师类
class Teacher :IInfo
{
public string Name;
public string GetName()
{
return Name;
}
public int Age;
public string GetAge()
{
return Age.ToString();
}
}
//声明实现了接口的学生类
class Student : IInfo
{
public string FirstName;
public string LastName;
public double PersonsAge;
public string GetName()
{
return FirstName + LastName;
}
public string GetAge()
{
return PersonsAge.ToString();
}
}
【例2】
using System;
namespace Test_CSharp
{
public interface IKouLanable
{
void KouLan();
}
public class Person
{
public void CHLSS()
{
Console.WriteLine("吃喝拉撒睡");
}
}
public class NBAPlayer
{
public void KouLan()
{
Console.WriteLine("我会扣篮");
}
}
public class Student : Person, IKouLanable
{
public void KouLan()
{
Console.WriteLine("我也会扣篮");
}
}
class Program
{
static void Main(string[] args)
{
//接口就是一个规范、能力。
}
}
}
接口的语法特征:
public interface IFlyable
{
//接口中的成员不允许添加访问修饰符 ,默认就是public
void Fly();
//不允许写具有方法体的函数
string Test();
//接口不能包含实例字段
string Name
{
get;
set;
}
}
自动属性和普通属性:
public class Person
{
private string _name;
//普通属性
public string Name
{
get { return _name; }
set { _name = value; }
}
//自动属性
public int Age
{
get;
set;
}
}
接口特点:
using System;
namespace Test_CSharp
{
public interface IFlyable
{
//不允许有访问修饰符,默认为public
//方法、自动属性
void Fly();
}
public class Bird : IFlyable
{
public void Fly()
{
Console.WriteLine("鸟在飞");
}
}
public class Person : IFlyable
{
public void Fly()
{
Console.WriteLine("人在飞");
}
}
public class Student
{
public void Fly()
{
Console.WriteLine("人在飞");
}
}
class Program
{
static void Main(string[] args)
{
IFlyable fly = new Bird();//new Person();
fly.Fly();
Console.ReadKey();
}
}
public interface M1
{
void Test1();
}
public interface M2
{
void Test2();
}
public interface M3
{
void Test3();
}
public interface SupperInterface : M1, M2, M3
{
}
public class Car : SupperInterface
{
public void Test1()
{
throw new NotImplementedException();
}
public void Test2()
{
throw new NotImplementedException();
}
public void Test3()
{
throw new NotImplementedException();
}
}
}
接口练习:
using System;
namespace Test_CSharp
{
//麻雀会飞 鹦鹉会飞 鸵鸟不会飞 企鹅不会飞 直升飞机会飞
//用多态来实现
//需方法、抽象类、接口
public interface IFlyable
{
void Fly();
}
public interface ISpeak
{
void Speak();
}
public class Bird
{
public double Wings
{
get;
set;
}
public void EatAndDrink()
{
Console.WriteLine("会吃会喝");
}
}
public class MaQue : Bird, IFlyable
{
public void Fly()
{
Console.WriteLine("麻雀会飞");
}
}
public class YingWu : Bird, IFlyable, ISpeak
{
public void Fly()
{
Console.WriteLine("鹦鹉会飞");
}
public void Speak()
{
Console.WriteLine("鹦鹉可以学习人类说话");
}
}
public class TuoBird : Bird
{
}
public class QQ : Bird
{
}
public class Plane : IFlyable
{
public void Fly()
{
Console.WriteLine("直升飞机转动螺旋桨飞行");
}
}
public
class Program
{
static void Main(string[] args)
{
IFlyable fly = new Plane();//new MaQue();//new YingWu();
fly.Fly();
Console.ReadKey();
}
}
}
显式实现接口:
using System;
namespace Test_CSharp
{
class Program
{
static void Main(string[] args)
{
//显式实现接口就是为了解决方法的重名问题
IFlyable fly = new Bird();
fly.Fly();
Bird bird = new Bird();
bird.Fly();
Console.ReadKey();
}
}
public class Bird : IFlyable
{
public void Fly()
{
Console.WriteLine("鸟飞会");
}
/// <summary>
/// 显式实现接口
/// </summary>
void IFlyable.Fly()
{
Console.WriteLine("我是接口的飞");
}
}
public interface IFlyable
{
void Fly();
}
}
实现多个接口:
(1)类或结构可以实现任意数量的接口;
(2)所有实现的接口必须列在基类列表中,并以逗号分隔(如果有基类名称,则在其之后)。
using System;
class Program
{
static void Main(string[] args)
{
MyData data = new MyData();
data.SetData(5);
Console.WriteLine("Value={0}", data.GetData());
}
}
//声明接口
interface IDataRetrieve
{
int GetData();
}
//声明接口
interface IDataStore
{
void SetData(int x);
}
//实现接口
class MyData : IDataRetrieve, IDataStore
{
int Mem1;
public int GetData()
{
return Mem1;
}
public void SetData(int x)
{
Mem1 = x;
}
}
实现具有重复成员的接口:
using System;
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
mc.PrintOut("object");
}
}
interface IIfc1
{
void PrintOut(string s);
}
interface IIfc2
{
void PrintOut(string t);
}
//两个接口的单一实现
class MyClass:IIfc1,IIfc2
{
public void PrintOut(string s)
{
Console.WriteLine("Calling through:{0}", s);
}
}
10.超市收银系统
using System;
using System.Collections.Generic;
namespace 超市收银系统
{
//商品的父类(三个自动属性:ID、价格、名称)
class ProductFather
{
/*自动属性:
* 1.自动实现的属性必须同时声明 get和 set 访问器。创建 readonly自动实现属性时,需要将set访问器设置为private 。
* 2.自动实现的属性上可以使用特性,不能用在支持后备字段上。如果属性的后备字段上使用特性,则应该只创建常规属性。
* 3.自动实现属性get,和set中不能包含特殊的逻辑处理。与字段类似,但不同于字段。与字段不同,属性不作为变量来分类,不能将属性作为 ref参数或 out参数传递。
*/
public string ID
{
get;
set;
}
public double Price
{
get;
set;
}
public string Name
{
get;
set;
}
//构造函数:帮助我们初始化对象(给对象的每个属性依次的赋值)。
public ProductFather(string id, double price, string Name)
{
this.ID = id;
this.Price = price;
this.Name = Name;
}
}
//宏基电脑
class Acer : ProductFather
{
public Acer(string id, double price, string name)
: base(id, price, name)
{
}
}
//三星手机
class SamSung : ProductFather
{
public SamSung(string id, double price, string name)
: base(id, price, name)
{
}
}
//酱油
class JiangYou : ProductFather
{
public JiangYou(string id, double price, string name)
: base(id, price, name)
{
}
}
//香蕉
class Banana : ProductFather
{
public Banana(string id, double price, string name)
: base(id, price, name)
{
}
}
//打折的父类(抽象类:父类中的方法没有默认实现,父类也不需要被实例化)
abstract class CalFather
{
/// <summary>
/// 计算打折后应付多少钱
/// </summary>
/// <param name="realMoney">打折前应付的钱</param>
/// <returns>打折后应付的钱</returns>
public abstract double GetTotalMoney(double realMoney);
}
//不打折,该多少钱就多少钱
class CalNormal : CalFather
{
public override double GetTotalMoney(double realMoney)
{
return realMoney;
}
}
//满减优惠:满M元,减N元
class CalMN : CalFather
{
//自动属性
public double M
{
get;
set;
}
public double N
{
get;
set;
}
public CalMN(double m, double n)
{
this.M = m;
this.N = n;
}
public override double GetTotalMoney(double realMoney)
{
if (realMoney >= this.M)
{
return realMoney - (int)(realMoney / this.M) * this.N;
}
else
{
return realMoney;
}
}
}
//按折扣率打折
class CalRate : CalFather
{
//折扣率
public double Rate
{
get;
set;
}
public CalRate(double rate)
{
this.Rate = rate;
}
public override double GetTotalMoney(double realMoney)
{
return realMoney * this.Rate;
}
}
//仓库
class CangKu
{
//集合
List<List<ProductFather>> list = new List<List<ProductFather>>();
/*list[0]存储Acer电脑
*list[1]存储三星手机
*list[2]存储酱油
*list[3]存储香蕉 */
//在创建仓库对象的时候,向仓库中添加4层货架
public CangKu()
{
list.Add(new List<ProductFather>());//list[0] 第一层货架(父类:商品)
list.Add(new List<ProductFather>());//list[1] 第二层货架(父类:商品)
list.Add(new List<ProductFather>());//list[2] 第三层货架(父类:商品)
list.Add(new List<ProductFather>());//list[3] 第四层货架(父类:商品)
}
//进货(两个参数:商品类型,商品数量)
public void JinPros(string strType,int count)
{
for(int i=0;i<count;i++)
{
switch(strType)
{
case "Acer"://第一层货架添加价值1000的宏基笔记本
list[0].Add(new Acer(Guid.NewGuid().ToString(), 1000, "宏基笔记本"));//Guid.NewGuid():全球唯一标识符(永远不会重复)
break;
case "SamSung"://第二层货架添加价值2000的三星手机
list[1].Add(new SamSung(Guid.NewGuid().ToString(), 2000, "三星手机"));
break;
case "JiangYou"://第三层货架添加价值10的老抽酱油
list[2].Add(new JiangYou(Guid.NewGuid().ToString(), 10, "老抽酱油"));
break;
case "Banana"://第四层货架添加价值50的大香蕉
list[3].Add(new Banana(Guid.NewGuid().ToString(), 50, "大香蕉"));
break;
}
}
}
//从仓库中取货(两个参数:商品类型,商品数量)返回类型:商品父类数组
public ProductFather[] QuPros(string strType,int count)
{
ProductFather[] pros = new ProductFather[count];
for(int i=0;i<pros.Length;i++)
{
switch(strType)
{
case "Acer"://从第一层货架上取出宏基笔记本
pros[i] = list[0][0];
list[0].RemoveAt(0);
break;
case "SamSung"://从第二层货架上取出三星手机
pros[i] = list[1][0];
list[1].RemoveAt(0);
break;
case "JiangYou"://从第三层货架上取出老抽酱油
pros[i] = list[2][0];
list[2].RemoveAt(0);
break;
case "Banana"://从第四层货架上取出大香蕉
pros[i] = list[3][0];
list[3].RemoveAt(0);
break;
}
}
return pros;
}
//向用户展示货物
public void ShowPros()
{
foreach (var item in list)
{
Console.WriteLine("我们超市有:" + item[0].Name + "," + "\t" + "有" + item.Count + "个," + "\t" + "每个" + item[0].Price + "元");
}
}
}
//超市
class SupperMarket
{
//创建仓库对象
CangKu ck = new CangKu();
//创建超市对象的时候,给仓库的货架上导入货物
public SupperMarket()
{
ck.JinPros("Acer", 1000);
ck.JinPros("SamSung", 1000);
ck.JinPros("JiangYou", 1000);
ck.JinPros("Banana", 1000);
}
//根据用户选择的打折方式返回一个打折对象
public CalFather GetCal(string input)
{
CalFather cal = null;
switch(input)
{
case "1":
cal = new CalNormal();
break;
case "2":
cal = new CalRate(0.9);
break;
case "3":
cal = new CalRate(0.85);
break;
case "4":
cal = new CalMN(300, 50);
break;
case "5":
cal = new CalMN(500, 100);
break;
}
return cal;
}
//根据用户买的货物计算总价格
public double GetMoney(ProductFather[] pros)
{
double realMoney = 0;
for(int i=0;i<pros.Length;i++)
{
realMoney += pros[i].Price;
}
return realMoney;
}
//展示超市里的商品
public void ShowPros()
{
ck.ShowPros();
}
// 跟用户交互的过程
public void AskBuying()
{
Console.WriteLine("欢迎光临,请问您需要些什么?");
Console.WriteLine("我们有 Acer、SamSung、Jiangyou、Banana");
string strType = Console.ReadLine();
Console.WriteLine("您需要多少?");
int count = Convert.ToInt32(Console.ReadLine());//将用户输入的字符串强转为int
//去仓库取货物
ProductFather[] pros = ck.QuPros(strType, count);
//下面该计算价钱了
double realMoney = GetMoney(pros);
Console.WriteLine("您总共应付{0}元", realMoney);
Console.WriteLine("请选择您的打折方式 1--不打折 2--打九折 3--打85 折 4--买300送50 5--买500送100");
string input = Console.ReadLine();
//通过简单工厂的设计模式根据用户的输入获得一个打折对象
CalFather cal = GetCal(input);
double totalMoney = cal.GetTotalMoney(realMoney);
Console.WriteLine("打完折后,您应付{0}元", totalMoney);
Console.WriteLine("以下是您的购物信息");
foreach (var item in pros)
{
Console.WriteLine("货物名称:" + item.Name + "," + "\t" + "货物单价:" + item.Price + "," + "\t" + "货物编号:" + item.ID);
}
}
}
class Program
{
static void Main(string[] args)
{
//创建超市对象
SupperMarket sm = new SupperMarket();
//展示货物
sm.ShowPros();
//跟用户交互
sm.AskBuying();
Console.ReadKey();
}
}
}
11.GUID
产生一个不会重复的编号。
class Program
{
static void Main(string[] args)
{
//产生一个不会重复的编号
Console.WriteLine(Guid.NewGuid().ToString());
Console.WriteLine(Guid.NewGuid().ToString());
Console.WriteLine(Guid.NewGuid().ToString());
Console.WriteLine(Guid.NewGuid().ToString());
Console.WriteLine(Guid.NewGuid().ToString());
Console.ReadKey();
}
}
十一、Winform基础
1. 概念
Winform应用程序是一种智能客户端技术,我们可以使用winform应用程序帮助我们获得信息或者传输信息等。
2. 属性
Name:在后台要获得前台的控件对象,需要使用Name属性;
Visible:指示一个控件是否可见;
Enabled:指示一个控件是否可用。
3. 事件:发生一件事情。
注册事件:双击控件注册的都是控件默认被选中的那个事件。
触发事件;
4. 在Main函数当中创建的窗体对象,我们称之为这个窗体应用程序的主窗体,
也就意味着,当你将主窗体关闭后,整个应用随之关闭。
例子: 点击“爱”和“不爱”小游戏。
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
/// <summary>
/// 当鼠标进入按钮可见部分的时候,给按钮一个新的坐标
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_unlove_MouseEnter(object sender, EventArgs e)
{
//给按钮一个新的坐标
//这个按钮活动的最大宽度就是 窗体的宽度减去按钮的宽度
int x = this.ClientSize.Width - btn_unlove.Width;
int y = this.ClientSize.Height - btn_unlove.Height;
Random r = new Random();
//要给按钮一个随机的坐标
btn_unlove.Location = new Point(r.Next(0, x + 1), r.Next(0, y + 1));
}
private void btn_love_Click(object sender, EventArgs e)
{
MessageBox.Show("I love you too");
this.Close();//关闭主窗体
}
private void btn_unlove_Click(object sender, EventArgs e)
{
MessageBox.Show("还是被你点到了");
this.Close();//关闭主窗体
}
}
}
运行结果:
5. TextBox控件
(1)常用属性:
WorldWrap:指示文本框是否换行;
PasswordChar:让文本框显示一个单一的字符;
ScrollBars:是否显示滚动条;
(2)常用事件:
TextChanged:当文本框中的内容发生改变的时候触发这个事件。
TextBox示例如下:
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
/// <summary>
/// 当文本框TextBox中的内容发生改变的时候,将内容赋值给label
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void txtBox_TextChanged(object sender, EventArgs e)
{
label.Text = txtBox.Text;
}
}
}
运行结果:
6. Timer组件
在指定的时间间隔内做一件指定的事情。
跑马灯练习:
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
label.Text = label.Text.Substring(1) + label.Text.Substring(0, 1);
}
}
}
运行结果:
闹钟练习:
using System;
using System.Media;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
/// <summary>
/// 当窗体加载出来的时候 将系统的时间赋值给label
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
label_Time.Text = DateTime.Now.ToString();
}
/// <summary>
/// 每隔一秒钟就把当前的时间赋值给label
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer1_Tick(object sender, EventArgs e)
{
label_Time.Text = DateTime.Now.ToString();
//设置一个固定的时间点作为闹钟
if(DateTime.Now.Hour==18&&DateTime.Now.Minute==13&&DateTime.Now.Second==10)
{
//播放音乐
SoundPlayer sp = new SoundPlayer();
sp.SoundLocation = @"C:\音效01_科幻.wav";//播放本地音乐
sp.Play();
}
}
}
}
运行结果:
十二、多线程和Socket网络编程
人通过【电话】可以通信,
程序通过【Socket】进行通信,
Socket(套接字)就是程序间的电话机。
甲和乙打电话(规定好的语言);
电脑和电脑进行联系(协议)。
Socket相关概念
Socket的英文原义是“孔”或“插座”。作为进程通信机制,取后一种意思。通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄。(其实就是两个程序通信用的)
Socket非常类似于电话插座。以一个电话网为例。电话的通话双方相当于相互通信的两个程序,电话号码就是IP地址。任何用户在通话之前,首先要占有一部电话机,相当于申请一个Socket;同时要知道对方的号码,相当于对方有一个固定的socket。然后向对方拨号呼叫,相当于发出连接请求。对方假如在场并空闲,拿起电话话筒,双方就可以正式通话,相当于连接成功。双方通话的过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向Socket发送数据和从Scoket接收数据。通话结束后,一方挂起电话机相当于关闭Socket,撤销连接。