介绍
C#学习过程中的知识点总结
使用说明
- 本文档内容大多从网络学习中总结归纳出的知识点,如有侵权,请联系删除
- 链接表示当前问题所解决的途径或者是当前知识点所在的网站
- 如有错误或者不足的地方,希望您可以与本人联系,进行补充与修改(邮箱、留言等方式)
/**********************************************************
Copyright: 内蒙古工业大学@Inner Mongol University of Technology
Project name: C#知识点总结
Learning Web Site: https://www.bilibili.com/video/BV1FJ411W7e5?p=1&t=2.3
Programmer:武佳
Version: 1.0
Start Time: 2021/11/08
Finish Time: 2021/11/26
History: None
**********************************************************/
1.输出Hello,World
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("hello world");
Console.ReadKey();//使程序暂停
}
}
}
2.C#中常用快捷键
End:使鼠标光标移动到当前语句最后(键盘右上角或者快捷键shift+1)
Home:使鼠标光标移动到当前语句最后(快捷键shift+7)
Ctrl+k+d:快速对齐代码
Ctrl+k+c:对所选代码进行注释(注:长按ctrl后按下k,之后松开k,按下c,并不是三个键同时按)
Ctrl+k+u:对所选代码取消注释
#region #endregion:对代码进行折叠(在需要折叠的代码最前面写#region,最后面写#endregion)
///: 对程序进行注释说明
///
/// 计算两个整数之间的最大值并且将最大值返回
///
/// 第一个整数
/// 第二个整数
/// 返回最大值
public static int MAX(int n1,int n2)
{
return n1>n2 ? n1: n2;
}
3.数据类型
Char:有且只能存储单字符,用单引号’ ’进行引用(注:不能存储空字符和多字符)char c = ‘a’;
String:可以存储任意字符及字符串,用双引号” ”进行引用 string Name = “Tom”;
Decimal:如果希望实数被视为 decimal 类型,请使用后缀 m 或 M,例如:decimal myMoney = 300.5m;
decimal拥有比float更高的精度,最高能处理到小数点后面的28位。适合用在财务类等对数字精确度要求比较高的场合。
参考网站:https://blog.youkuaiyun.com/u010771437/article/details/40867831/
4.变量命名规范
1Camel 骆驼命名规范:要求变量名首单词的首字母要小写,其余每个单词的首宇母要大写,多用于给变量命名
举例:findLine
Paseal 命名规范:要求每个单词的首字母都要大写,其余字母小写,多用于给类或者方法命名
举例:FindLine
5.+号的使用
- 当+号有一边是字符串的时候,可以将双边字符串相连接,相当于c语言中strcat函数
伪代码:
Static void Main(string[] args)
{
String name = “zhangsan”
Console.WriteLine(“hello,”+name);
Console.ReadKey();
}
输出结果为:hello,zhangsan
2)当+号两边是数字的时候,起到相加的作用
伪代码:
Static void Main(string[] args)
{
Console.WriteLine(5 + 5);
Console.ReadKey();
}
输出结果为10(注:如果改为Console.WriteLine(5+”5”);结果为55)
6.占位符的使用
要输出的数据可以用{0},{1}……表示
伪代码:
static void Main(string[] args)
{
double n1 = 12.345, n2 = 10;
Console.WriteLine("第一个数字是" + n1 + ",第二个数字是" + n2);
Console.WriteLine("第一个数字是{0:0.00},第二个数字是{1}", n1, n2);//:0.00表示取两位余数
Console.ReadKey();
}
7.WriteLine的使用
将要输入的数据使用string存到字符串中
伪代码:
static void Main(string[] args)
{
int n1 = 10, n2 = 20;
Console.WriteLine("请输入您的姓名");
string name = Console.ReadLine();//使用string存储
Console.WriteLine("您的姓名是{0}",name);
Console.ReadKey();
}
8.转义符
-
\” 表示一个英文半角的双引号
-
\ 表示一个\
-
\b 表示一个退格键,放到字符串的两边没有效果(也就是删除前面一个字符)
-
\r\n 表示跳到下一行,并且返回到下一行的起始位置。(注:当需要将vs中写的文本内容传入到桌面上时,使用\r\n,Windows操作系统只认识\r\n,而苹果系统只需要写\n即可)
伪代码:static void Main(string[] args)
{
string str = “今天星期日\r\n天气真的冷”;
System.IO.File.WriteAllText(@“C:\Users\晨曦\Desktop\111.txt”,str);//@后面的路径查找方法是右击桌面任意TXT文件,查看属性
Console.WriteLine(“写入成功!”);
Console.ReadKey();
}
5)@符号
(1)取消\在字符串的转义作用
\如果不加@则程序会报错,如果想在不加@的情况下使语句正确输出,则需要改为string path = @“C\user\chenxi\Desktop”;
string path = @"C\user\chenxi\Desktop";
Console.WriteLine("path");
(2)将字符串按照原格式输出
//在字符串“今天星期日”后面进行回车,如果不加@,程序会报错,当加@之后,程序会自动进行换行操作,并且不报错
Console.WriteLine(@“今天星期日
不需要上课”);Console.ReadKey();
9.类型转换
- 显式类型转换:要求是两种类型相兼容,并且是大范围的类型转换成小范围类型(double->int)
static void Main(string[] args)
{
double n = 303.6;
int m = (int)n;
Console.WriteLine(m);
Console.ReadKey();
}
- 隐式类型转换:要求是两种类型相兼容,并且是小范围的类型转换大范围类型(int->idouble)
static void Main(string[] args)
{
int n = 303;
double m = n;
Console.WriteLine(m);
Console.ReadKey();
}
- Convert转换:在两种类型不兼容的情况下,可以进行强制转换
static void Main(string[] args)
{
string n = "123.4";
double m = Convert.ToDouble(n);
Console.WriteLine(m);
Console.ReadKey();
}
10.一元运算符和二元运算符
程序举例:
static void Main(string[] args)
{
int a = 5;
int b = a++ + ++a * 2 + --a + a++;//5+7*2+6+6=31 a=7
Console.WriteLine(a);
Console.WriteLine(b);
Console.ReadKey();
}
从上面程序分析得到,在C#中,运算符的优先级并不是“先加减,后乘除”,而是先进行一元运算符的运算,再进行二元运算符的运算
对比C语言:
int main(){
int a = 5;
//int b = a++ + ++a * 2 ;//6+6*2=18 a=7
//int c = a++ + ++a*2 + --a; //6+6*2+5=23 a=6
int d = a++ + ++a * 2 + --a + a++; //6+6*2+5+7=28 a=7
printf("%d\n%d\n", a, d);
return 0;
}
11.异常捕获
出现异常说明语法上没有错误,而是在程序运行过程中,由于某些原因程序出现了错误,不能正常运行,因此在可能出现异常的语句使用try-catch,进行异常捕获
程序举例:
static void Main(string[] args)
{
int number;
Console.WriteLine("请输入您想要的数字");
try
{
number = Convert.ToInt32(Console.ReadLine());
Console.WriteLine(number*2);
}
catch
{
Console.WriteLine("您输入的为非法数字");
}
Console.ReadKey();
}
12.调试的2种方法
- 直接按键盘的F11键,进行逐步调试,并且可以在上方工具栏“调试”->“窗口”->“监视”,进行程序语句的实时监视。
- 在界面的左边栏设置断点,其作用是使程序执行到当前断点语句停止,之后按下F11进行逐行调试工作。
13.枚举
- 枚举类型默认可以和int类型互相转换,枚举类型跟int类型是兼容的,可以将枚举类型转换为int类型,也可以将int类型转换为枚举类型
- 在枚举类型中,默认第一个成员变量的值为“0”,其他成员变量按照从上到下的顺序依次加1,如果给第一个成员变量赋值为“1”,则其他成员变量也会依次增1
- 和C语言中枚举的区别是,不需要在public enum{}花括号后面加“;”分号
程序举例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
public enum QQState
{
OnLine,
OffLine,
Leave,
Busy
}
class Program
{
static void Main(string[] args)
{
int m = 3;
QQState state1 = (QQState)m;//int->枚举
Console.WriteLine(state1);//输出结果为Busy
QQState state2 = QQState.OnLine;
int n = (int)state2;//枚举->int
Console.WriteLine(n);//输出结果为0
Console.WriteLine((int)QQState.Busy);//输出结果为3
Console.ReadKey();
}
}
}
14.ToString的使用
所有的类型都能够转换为string类型
static void Main(string[] args)
{
int n1 = 10;
decimal n2 = 250m;
string s = n1.ToString();
string m = n2.ToString();
QQState state = QQState.OnLine;
string x = state.ToString();
Console.WriteLine(s);
Console.WriteLine(m);
Console.WriteLine(x);
Console.ReadKey();
}
15.数组
Int [] nums = new int[5];//存储的初值都是0,占用20个字节
String[] str = new string[5];//存储的是“NULL”,不占用字节
Bool[] bools = new bool[5];//存储的是“false”,占用5个字节
16.out参数
如果在一个方法(函数)中,要返回多个不同类型的值,使用out参数,类似于C语言中的结构体变量,但在C#的结构中,只能返回相同类型的值,因此引入out参数
程序举例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args){
Console.WriteLine("请输入用户名");
string User = Console.ReadLine();
Console.WriteLine("请输入密码");
string Password = Console.ReadLine();
string Message;//不需要赋值
bool b = IsLogin(User, Password, out Message);
Console.WriteLine("信息{0}",b);
Console.WriteLine("{0}",Message);
Console.ReadKey();
}
/// <summary>
/// 存储一个用户名和密码,最后使用out参数可以获得返回的信息
/// </summary>
/// <param name="User">用户名</param>
/// <param name="Password">密码</param>
/// <param name="Message">登录信息</param>
/// <returns>返回结果</returns>
public static bool IsLogin(string User,string Password,out string Message)
{
if(User == "wujia"&&Password == "1234")
{
Message = "登录成功";
return true;
}
else if (User == "wujia")
{
Message = "密码错误";
return false;
}
else
{
Message = "未知错误";
return false;
}
}
}
}
17.ref关键字
1)使用带ref的方法之前,需要给方法参变量赋上初始值
2)无论是定义方法还是使用方法,都要带上关键字ref
伪代码(交换两数):
static void Main(string[] args){
int n1 = 10;
int n2 = 20;
IsLogin(ref n1, ref n2);
Console.WriteLine(n1);
Console.WriteLine(n2);
Console.ReadKey();
}
public static void IsLogin(ref int n1,ref int n2)
{
int temp = n1;
n1 = n2;
n2 = temp;
}
18.params可变参数
作用:可以对数组元素进行增删
注意:
1)参数数组必须是一维数组
2)params参数必须是参数表的最后一个参数
3)不允许将params修饰符与ref和out修饰符组合起来使用
4) 与参数数组对应的实参可以是同一类型的数组名,也可以是任意多个与该数组的元素属于同一类型的变量
5) 若实参是数组则按引用传递,若实参是变量或表达式则按值传递
程序举例:
class Program
{
static void Main(string[] args){
int[] nums = { 1, 2, 3, 4 };//int类型的一维数组
int sum1 = GetSum(nums);
Console.WriteLine(sum1);
int sum2 = GetSum(5,2,3);//多个任意int类型变量
Console.WriteLine(sum2);
Console.ReadKey();
}
public static int GetSum(params int[] n)
{
int sum=0;
for (int i = 0; i < n.Length; i++)
{
sum += n[i];
}
return sum;
}
}
19.方法的重载(前提条件为:方法的名称必须相同)
- 如果参数的个数相同,那么参数的类型就不能相同
- 如果参数的类型相同,那么参数的个数就不能相同
- 方法的重载和返回值没有任何关系
20.飞行棋项目
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 飞行棋游戏
{
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];//Flag{0}默认是flase
static void Main(string[] args)
{
GameShow();
#region 输入玩家姓名
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("请输入玩家A的姓名");
PlayerNames[0] = Console.ReadLine();
while (PlayerNames[0] == "")
{
Console.WriteLine("玩家姓名不能为空,请重新输入");
PlayerNames[0] = Console.ReadLine();
}
/**************************************/
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("请输入玩家B的姓名");
PlayerNames[1] = Console.ReadLine();
while (PlayerNames[1] == ""|| PlayerNames[0] == PlayerNames[1])
{
if(PlayerNames[1] == "")
{
Console.WriteLine("玩家姓名不能为空,请重新输入");
PlayerNames[1] = Console.ReadLine();
}
else
{
Console.WriteLine("玩家A与玩家B姓名重复,请重新输入");
PlayerNames[1] = Console.ReadLine();
}
}
#endregion
//玩家姓名输入完毕之后,应该首先清屏
Console.Clear();
GameShow();
Console.WriteLine("{0}的士兵用A表示", PlayerNames[0]);
Console.WriteLine("{0}的士兵用B表示", PlayerNames[1]);
InitailMap();//初始化地图资源
DrawMap();
//当两名玩家都没有到达终点时,游戏会一直进行
while (PlayerPos[0] < 99 && PlayerPos[1] < 99)
{
if(Flags[0] == false)
{
PlayGame(0);
}
else
{
Flags[0] = false;
}
if(PlayerPos[0]>=99)
{
Console.WriteLine("玩家{0}获得胜利!",PlayerNames[0]);
break;
}
if (Flags[1] == false)
{
PlayGame(1);
}
else
{
Flags[1] = false;
}
if (PlayerPos[1] >= 99)
{
Console.WriteLine("玩家{0}获得胜利!", PlayerNames[1]);
break;
}
}//while
Console.ReadKey();
}
#region 飞行棋游戏头界面
public static void GameShow()
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("********************");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("********************");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("*****飞行棋游戏*****");
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("********************");
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("********************");
}
#endregion
#region 初始化地图资源
public static void InitailMap()
{
#region 幸运轮盘
int[] LuckyTurn = {6,23,40,55,69,83};
for(int i = 0; i < LuckyTurn.Length; i++)
{
int index = LuckyTurn[i];
Maps[index] = 1;
//Maps[LuckyTurn[i]] = 1;.//将index用LuckyTurn[i]替换
}
#endregion
#region 地雷
int[] LandMine = {5,13,17,33,38,50,64,80,94};
for (int i = 0; i < LandMine.Length; i++)
{
int index = LandMine[i];
Maps[index] = 2;
}
#endregion
#region 暂停
int[] Pause = {9,27,60,93};
for (int i = 0; i < Pause.Length; i++)
{
int index = Pause[i];
Maps[index] = 3;
}
#endregion
#region 时空隧道
int[] TimeTunnel = {20,25,45,63,72,88,90};
for (int i = 0; i < TimeTunnel.Length; i++)
{
int index = TimeTunnel[i];
Maps[index] = 4;
}
#endregion
}
#endregion
#region 搭建地图
public static void DrawMap()
{
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("图例:幸运轮盘:◎ 地雷:☆ 暂停:▲ 时空隧道:→");
#region 第一横行
for (int i=0; i<30;i++)
{
Console.Write(DrawStringMap(i));
}
#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>34;i--)
{
Console.Write(DrawStringMap(i));
}
#endregion
Console.WriteLine();//画完第二横行要换行
#region 第二竖行
for (int i = 65; i < 70; i++)
{
Console.Write(DrawStringMap(i));
Console.WriteLine();
}
#endregion
#region 第三横行
for (int i = 70; i < 100; i++)
{
Console.Write(DrawStringMap(i));
}
#endregion
Console.WriteLine();//画完第三横行要换行
}//DrawMap()
#endregion
#region 调用地图中某一元素
public static string DrawStringMap(int i)
{
string str = "";
#region 画图
//如果玩家A很玩家B的坐标相同,并且都在地图上,画一个尖括号
if (PlayerPos[0] == PlayerPos[1] && PlayerPos[0] == i)
{
str = "<>";
}
else if (PlayerPos[0] == i)
{
str = "A";
}
else if (PlayerPos[1] == i)
{
str = "B";
}
else
{
switch (Maps[i])
{
case 0:
Console.ForegroundColor = ConsoleColor.Red;
str = "□";
break;
case 1:
Console.ForegroundColor = ConsoleColor.Cyan;
str = "◎";
break;
case 2:
Console.ForegroundColor = ConsoleColor.Green;
str = "☆";
break;
case 3:
Console.ForegroundColor = ConsoleColor.Yellow;
str = "▲";
break;
case 4:
Console.ForegroundColor = ConsoleColor.Magenta;
str = "→";
break;
}//switch
}//else
return str;
#endregion
}
#endregion
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);
//玩家{0}有可能踩到玩家{1} 方块 幸运轮盘 地雷 暂停 时空隧道
if (PlayerPos[PlayerNumber] == PlayerPos[1-PlayerNumber])
{
Console.WriteLine("玩家{0}踩到了玩家{1},玩家{2}退六格", PlayerNames[PlayerNumber], PlayerNames[1- PlayerNumber], PlayerNames[1- PlayerNumber]);
PlayerPos[1- PlayerNumber] -= 6;
ChangePos();
Console.ReadKey(true);
}
else//踩到了关卡
{
switch (Maps[PlayerPos[PlayerNumber]])
{
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}退六格", PlayerNames[PlayerNumber], PlayerNames[1- PlayerNumber], PlayerNames[1- PlayerNumber]);
Console.ReadKey(true);
PlayerPos[1- PlayerNumber] -= 6;
ChangePos();
Console.WriteLine("玩家{0}退了六格", PlayerNames[1- PlayerNumber]);
Console.ReadKey(true);
break;
}
else
{
Console.WriteLine("只能输入1或者2!请选择1--交换位置 2--轰炸对方");
input = Console.ReadLine();
}
}
break;
case 2:
Console.WriteLine("玩家{0}踩到了地雷,后退六格", 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}踩到了时空隧道,前进十格", PlayerNames[PlayerNumber]);
Console.ReadKey(true);
PlayerPos[PlayerNumber] += 10;
ChangePos();
break;
}//switch
}//else
ChangePos();
Console.Clear();
DrawMap();
}
#region 如果越界,则返回起点/终点
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;
}
#endregion
}
}
}
21.面向对象
- This关键字:表示当前类的对象;类是不占用内存的(NULL),而对象会占用;C#中当new 一个类时,首先是对这个类中的字段进行初始化,然后调用构造方法,换言之,首先为类中的字段分配内存空间,然后执行构造方法;在方法中不加this不构成语法错误,但当此方法中含有同名局部变量时,会默认输出局部变量,而不是输出属性
- 类的声明时不能加static,类中的成员,如果不加访问修饰符,默认都是private
- 一些基础名词
字段:存储数据
属性:保护字段,对字段的取值和设值进行限定
方法:描述对象的行为
构造函数:初始化对象(给对象的每个属性依次的赋值)
22.属性
1)属性用于描述对象的静态特征,属性本身不保存数据,他只是外界访问私有字段的一个入口,其数据类型要和字段一致;而字段主要用于类的内部做数据交互使用,因此绝大多数都为private
2)可以在属性中的get和set访问器中添加约束代码,保障数据的有效性;由此看出,属性的本质就是两个方法
//类中程序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace initClassTest
{
class Student
{
public Student()
{
}
//定义私有字段
private string studentName = "";
private int studentAge = 0;
private int studentId = 0;
//通过属性的访问器访问和修改私有字段并对数据进行约束
public int StudentAge
{
get { return studentAge; }
set
{
if (studentAge <= 18)
studentAge = 18;
else
studentAge = value;
}
}
}
}
//主函数中程序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace initClassTest
{
class Program
{
static void Main(string[] args)
{
Student student = new Student();
student.StudentAge = -20;
Console.WriteLine(student.StudentAge);
Console.ReadKey();
}
}
}
23.静态与非静态的区别
- 在非静态类中,既可以有实例成员,也可以有静态成员
- 在调用实例成员时,需要使用对象名.实例成员;
在调用静态成员时,需要使用类名.静态成员名(class)
3)静态函数中,只能访问静态成员,不能访问实例成员
4)静态函数中,只允许有静态成员,不允许出现实例成员
24.析构函数
- 析构方法是在垃圾回收、释放资源时使用的
- 不能在结构中定义析构函数。只能对类使用析构函数
- 一个类只能有一个析构函数,无法继承或重载析构函数
- 无法调用析构函数。它们是被自动调用的,析构函数既没有修饰符,也没有参数
- 由于字符串(string)的不可变性,即使赋予字符串新的值,原值也会存储在堆中,内存并不会被释放,因此引入析构函数,用于释放内存
定义形式:
~方法名()
{
语句块;
}
25.构造函数
- 构造函数没有返回值,即使是void也不可以
- 函数名必须和类名一致
- 在主函数中调用,默认构造函数无参数,如果人为创建一个构造函数,原无参构造函数会消失
26.值类型和引用类型
值类型 称之为值传递,引用类型称之为引用传递。
区别:
1)值类型和引用类型在内存上存储的地方不一样.
2)在传递值类型和传递引用类型的时候,传递的方式不一样。
值类型: int. double. boo1. char. decimal、 struct. enum
引用类型: string、 自定义类
存储:
值类型的值是存储在内存的栈当中
引用类型的值是存储在内存的堆中
如图所示:
int number = 10;其存储在栈中
string s = “123”;,其存储于堆中,而其地址是存储在栈中
new Person();和Name = “张三”;存储在堆中,而其地址是存储在栈中
27.关于string类型
- string类型的数据由多个char类型数据组成
程序举例:
static void Main(string[] args)
{
string s = "abcde";
Console.WriteLine(s[0]);
Console.ReadKey();
}
输出结果为:a
- 可以将字符串看做是char类型的一个只读数组。
ToChararray();将字符串转换为Chars数组
new string(Chars);能够将Chars数组转换为字符串
程序举例:
static void Main(string[] args)
{
string s = "abcde";
//s[0] = 'f'; //由于是只读的,因此不能将f直接赋给s[0]
char[] Chars = s.ToCharArray();//该方法可以将字符串转换为char类型的数组
Chars[0] = 'f';
s = new string(Chars);//将字符数组转换为字符串
Console.WriteLine(s[0]);
Console.WriteLine(s);
Console.ReadKey();
}
- StringBuilder函数的使用
//记录所完成指定程序执行的时间,进行比较
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder();
string str = null;
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 100000; i++)
{
//str += i;//00:00:18.1619340
sb.Append(i);//00:00:00.0177886
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
Console.ReadKey();
}
由以上程序可以看出,调用StringBuilder函数可使程序执行时间大大缩短
- Contains
//对关键字进行屏蔽
static void Main(string[] args)
{
string str = "国家机密文档 ";
if (str.Contains("机密"))
{
str = str.Replace("机密", "**");
}
Console.WriteLine(str);
Console.ReadKey();
}
5)Substring
//截取字符串
static void Main(string[] args)
{
string str1 = "国家机密文档 ";
string str2 = "国家机密文档 ";
str1 = str1.Substring(1,2);
str2 = str2.Substring(1);
Console.WriteLine(str1);
Console.WriteLine(str2);
Console.ReadKey();
}
28.继承
- 继承的作用:在某些类中,写一些重复的成员,我们可以将这些重复的成员,单独的封装到一个类中,作为这些类的父类。
- 继承的分类为:一个父类基类:Person(课程中默认起名为Person),多个子类派生类:Student、Teacher. Driver;子类继承了父类,首先,子类继承了父类的属性和方法,但是子类并没有继承父类的私有字段。
- 思考一:子类有没有继承父类的构造函数?
答:子类并没有继承父类的构造函数,但是。子类会默认的调用父类无参数的构造函数,创建父类对象,让子类可以使用父类中的成员;所以,如果在父类中重新写了一个有参数的构造函数之后,那个无参数的就被干掉了,子类就调用不到了,所以子类会报错。
解决办法:
在父类中重新写一个无参数的构造函数或者是在子类中显示的调用父类的构造函数,使用关键字:base()
29.里氏转换
-
子类可以赋值给父类
Student stu = new Student();//创建了一个子类对象 Person p = stu;//赋值给父类 //Person p = new Student();//将以上两句合并
-
如果父类中装的是子类对象,那么可以讲这个父类强转为子类对象
Student Sstu = (Student)p; Sstu.StudentSayHello(); //((Student)p).StudentSayHello();//将以上两句合并
3)子类对象可以调用父类中的成员,但是父类对象永远都只能调用自己的成员
4)is和as的用法与区别
is:表示类型转换:如果能够转换成功,则返回一个true,否则返回一个false
as:表示类型转换:如果能够转换则返回对应的对象,否则返回一个null
里氏转换知识点的完整程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication8
{
class Program
{
static void Main(string[] args)
{
Student stu = new Student();//创建了一个子类对象
Person p = stu;//赋值给父类
//Person p = new Student(); //Person p = new Student();//将以上两句合并
//Student Sstu = (Student)p;//强制转换
//Sstu.StudentSayHello();
((Student)p).StudentSayHello();//将以上两句合并
//Console.ReadKey();
#region is和as的用法
/* if (p is Person)
{
Student std = (Student)p;
std.StudentSayHello();
}
else
{
Console.WriteLine("转换失败!");
}
Console.ReadKey();
Student s = p as Student;
s.StudentSayHello();
Console.ReadKey();
*/
#endregion
Person[] per = new Person[6];
Random r = new Random();
for(int i = 0; i < per.Length; i++)
{
int rNumber = r.Next(1, 4);
switch (rNumber)
{
case 1: per[i] = new Student();
break;
case 2: per[i] = new Teacher();
break;
case 3: per[i] = new Person();
break;
}
}
for(int i = 0; i < per.Length; i++)
{
if(per[i] is Student)
{
((Student)per[i]).StudentSayHello();
}
else if(per[i] is Teacher)
{
((Teacher)per[i]).TeacherSayHello();
}
else
{
per[i].PersonSayHello();
}
}
Console.ReadKey();
}
}
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("我是老师");
}
}
}
30.ArrayList集合的使用
- 各种方法的实现
程序举例:
ArrayList list = new ArrayList();
//添加单个元素
list.Add(true);
list.Add('猫');
list.Add("喵喵");
//添加集合元素
list.AddRange(new int[] { 1, 2, 3, 4, 5 });
//list.Clear();//清空所有元素
//list.Remove('男');//删除单个元素
//list.RemoveAt(2);//使用下标删除元素
//list.RemoveRange(0,2);
//list.Reverse();//使倒叙输出
//list.Insert(1, "插入元素");
bool back = list.Contains(8);//判断是否包含某个指定的元素
Console.WriteLine(back);
for(int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
Console.ReadKey();
- ArrayList集合的长度:每次集合中实际包含的元素个数(count)超过了可以包含的元素的个数(capcity)的时候,集合就会向内存中申请多开辟一倍的空间,来保证集合的长度一直够用。
伪代码:
ArrayList list = new ArrayList();
list.Add('猫');
list.Add('猫');
list.Add('猫');
Console.WriteLine(list.Count);
Console.WriteLine(list.Capacity);
Console.ReadKey();
31.Hashtable 键值对集合
- has.Add(1, “张三”); 数字1是键,字符串张三是值
- 在键值对集合中,是根据键去找值,键必须是唯一的,而值可以重复
- 键值对对象[键] = 值
程序举例:
static void Main(string[] args)
{
Hashtable has = new Hashtable();
has.Add(1, "张三");
has.Add(2, true);
has.Add(3, '男');
has.Add("ab", "b");
has.Add(true,"a");
has[6] = "可以以下标形式添加";
has[1] = "将张三替换";
foreach (var item in has.Keys)//利用foreach循环去遍历整个键值对
{
Console.WriteLine("键是----{0}----值是{1}", item, has[item]);
}
//for (int i = 1; i <has.Count+1; i++)//如果利用for循环,只要键不是数字,就无法遍历
//{
// Console.WriteLine(has[i]);
//}
//Console.WriteLine(has[1]);
//Console.WriteLine(has[2]);
Console.ReadKey();
}
32.Path类
Path类主要用于对路径字符串进行操作,并且提供了对路径操作的常用方法
程序举例:
static void Main(string[] args)
{
string str = @"D:\英雄时刻\6149075\test.docx";
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.ReadKey();
}
33.File类
- 对文件夹直接进行操作:创建、删除、读取、复制
程序举例:创建、删除、读取、复制
//File.Create(@“C:\Users\晨曦\Desktop\new.txt”);//创建一个文件
//File.Delete(@“C:\Users\晨曦\Desktop\new.txt”);
//string str = File.ReadAllText(@“C:\Users\晨曦\Desktop\new.txt”,Encoding.Default);
//Console.WriteLine(str);
File.Copy(@“C:\Users\晨曦\Desktop\openmv.txt”, @“C:\Users\晨曦\Desktop\new.txt”);
Console.ReadKey();
2)对文件夹进行解码
程序举例:按照指定的编码格式进行解码为字符串
//UTF-8 GB2312(国标简体) GBK(国标繁体和简体) ASCII Unicode(包含UTF32、UTF7、UTF8)
byte[] buffer = File.ReadAllBytes(@"C:\Users\晨曦\Desktop\openmv.txt");
string s = Encoding.GetEncoding("UTF-8").GetString(buffer);//解析
Console.WriteLine(s);
Console.ReadKey();
3)在文件夹中以字符的形式写入字符串
string str = "今天天气真的冷";
byte[] buffer = Encoding.Default.GetBytes(str);//需要将字符串转换为字节数组
File.WriteAllBytes(@"C:\Users\晨曦\Desktop\new.txt",buffer);
Console.WriteLine("写入成功!");
Console.ReadKey();
在文件夹中以行的形式写入字符串
string[] contents = File.ReadAllLines(@"C:\Users\晨曦\Desktop\程序.txt",Encoding.Default);
foreach (string item in contents)
{
Console.WriteLine(item);
}
Console.ReadKey();
34.List泛型集合
List一旦确定数据类型无法改变,并且长度也不能改变;而ArrayList是可以任意数据类型,长度也可以任意改变,但其添加的数据是object类型,如果需要调用,必须进行转换
程序举例:
static void Main(string[] args)
{
//List<int> list = new List<int>();
//list.Add(1);
//list.Add(2);
//list.Add(3);
//list.AddRange(new int[] { 4, 5, 6 });
//泛型集合转换为数组
//int[] nums = list.ToArray();
//for (int i = 0; i < list.Count; i++)
//{
// Console.WriteLine(nums[i]);
//}
//数组转换为泛型集合
int[] num = new int[] { 8,9};
List<int> listInt = num.ToList();
Console.WriteLine(listInt[1]);
Console.ReadKey();
}
35.Dictionary字典集合
程序举例:
static void Main(string[] args)
{
Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(1, "张三");
dic.Add(2, "李四");
dic.Add(3, "王五");
foreach (KeyValuePair<int,string> item in dic)//一对一对数据进行遍历
{
Console.WriteLine("{0}----{1}",item.Key,item.Value);
}
foreach (var item in dic.Keys)//遍历键
{
Console.WriteLine("{0}----{1}",item,dic[item]);
}//遍历键
Console.ReadKey();
}
36.关键字var
用var类型预先不用知道变量的类型;根据给变量赋值来判定变量属于什么类型
1)必须在定义时初始化,即不能先定义后初始化。如:var a;a=1;这样是不被允许的
2) 一旦初始化完成,不能再给变量赋与初始化不同的变量
3) var类型的变量必须是局部变量
37.练习:提示用户输入一个字符串,通过foreach循环用户输入的字符串赋值给一个字符串数组
程序实现:
static void Main(string[] args)
{
Console.WriteLine("请输入一个字符串");
string input = Console.ReadLine();
char[] ch = new char[input.Length];
int i = 0;
foreach (var item in input)
{
ch[i] = item;
i++;
}
foreach (var item in ch)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
38.FileStream文件流
1)
static void Main(string[] args)
{ //使用FileStream来读取数据
FileStream fsRead = new FileStream(@"C:\Users\晨曦\Desktop\程序.txt",FileMode.OpenOrCreate,FileAccess.Read);
byte[] buffer = new byte[1024 * 1024 * 5];
int re = fsRead.Read(buffer, 0, buffer.Length);
string s = Encoding.Default.GetString(buffer,0,re);
fsRead.Close();//关闭流
fsRead.Dispose();//释放内存
Console.WriteLine(s);
Console.ReadKey();
}
2)
static void Main(string[] args)
{ //使用FileStream来写入数据
using (FileStream fsWrite = new FileStream(@"C:\Users\晨曦\Desktop\test.txt", FileMode.OpenOrCreate, FileAccess.Write))
{
string str = "今天天气不太冷";
byte[] buffer = Encoding.Default.GetBytes(str);
fsWrite.Write(buffer, 0, buffer.Length);
}
Console.Write("写入OK!");
Console.ReadKey();
}
注意:
1)如果读取数据时发生乱码,修改语句“string s = Encoding.Default.GetString(buffer,0,re);”中的Encoding.后面内容
2)对比上述33.2
string s = Encoding.GetEncoding(“UTF-8”).GetString(buffer);
如果乱码,则修改括号内的编码格式
如果写入数据时发生乱码,修改语句“byte[] buffer = Encoding.Default.GetBytes(str);”中的Encoding.后面内容
39.StreamWriter和StreamReader
1)StreamReader用法程序举例:
using (StreamReader str = new StreamReader(@"C:\Users\晨曦\Desktop\new.txt", Encoding.Default))
{
while (!str.EndOfStream)//如果没有读取到最后一段数据,就一直读取
{
Console.WriteLine(str.ReadLine());
}
}
Console.ReadKey();
注意:while循环的目的是输出文件内所有的数据;如果发生乱码,在@"C:\Users\晨曦\Desktop\new.txt"后面加“Encoding.Default”
2)StreamWriter用法程序举例:
using (StreamWriter str = new StreamWriter(@"C:\Users\晨曦\Desktop\new.txt",true))
{
str.Write("今天晚上天气不太冷");
}
Console.WriteLine("写入成功!");
Console.ReadKey();
注意:默认写入数据后,覆盖原文件内数据,如果不想覆盖只想添加,在@"C:\Users\晨曦\Desktop\new.txt"后面加一个“true”
40.虚方法(多态)
利用virtual和override可以不需要强制转换,就能够输出语句;virtual写在父类里面,override写在子类里面,其优点就是可以预留接口,实现分工合作
对比上述29
程序举例:
namespace 多态
{
class Program
{
static void Main(string[] args)
{
Employee emp = new Employee();
Manager man = new Manager();
Programmer pro = new Programmer();
Employee[] emps = { emp, man, pro };
for(int i = 0; i < emps.Length; i++)
{
emps[i].PunchCard();
}
Console.ReadKey();
}
}
public class Employee
{
public virtual void PunchCard()
{
Console.WriteLine("九点打卡!");
}
}
public class Manager : Employee
{
public override void PunchCard()
{
Console.WriteLine("经理十一点打卡!");
}
}
public class Programmer : Employee
{
public override void PunchCard()
{
Console.WriteLine("程序员不打卡!");
}
}
}
40.抽象类(多态)
- 抽象成尽必须标记为abstract,并且不能有任何实现
- 抽象成员必须在抽象类中
- 抽家类不能被实例化
- 子类继承抽象类后,必须把父类中的所有抽象成员都重写。
(除非子类也是一个抽象类。则可以不重写) - 抽象成员的访问修饰符不能是private
- 在拍象类中可以包含害例成员
并且抽象类的实例成员可以不被子类实现 - 抽象类是有构造函数的。虽然不能被实例化
- 如果父类的抽象方法中有参数,那么。继承这个抽象父类的子类在重写父类的方法的时候必须传入对应的参数;如果抽象父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候也必须要传入返回值
- 如果父类中的方法有默认的实现,并且父类需要被实例化,这时可以考虑将父类定义成一个普通类,用虚方法来实现多态;如果父类中的方法没有默认实现,父类也不需要被实例化,则可以将该类定义为抽发类。
程序实例:
namespace 抽象类
{
class Program
{
static void Main(string[] args)
{
//使用多态求矩形的面积和周长以及圆形的面积和周长
//Shape shape = new Circle(5);//求半径为5圆的周长和面积
Shape shape = new Square(5,5);//求长宽都为5矩形的周长和面积
double area = shape.GetArea();
double perimter = shape.GetPerimeter();
Console.WriteLine("这个形状的面积是{0},周长是{1}",area,perimter);
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 _a;
public double A//属性用来使外界访问私有字段
{
get { return _a; }
set { _a = value; }
}
private double _b;
public double B//属性用来使外界访问私有字段
{
get { return _b; }
set { _b = value; }
}
public Square(double a, double b)//构造函数用来传入数据
{
this.A = a;
this.B = b;
}
public override double GetArea()
{
return this.A * this.B;
}
public override double GetPerimeter()
{
return (this.A + this.B) * 2;
}
}
}
41.C#中的访问修饰符
- public :公开的、公共的
- private:私有的,只能在当前类的内部访问
- protected:受保护的,只能在当前类的内部以及该类的子类中访问
- internal:只能在当前项目中访问。在同一个项目中,internal和public的权限是一 样
- protected internal: protected+ internal的权限
注意:
1)能够修饰类的访间修饰符只有两个: public、 internal.
2)子类的访间权限不能高于父类的访问权限,会暴漏父类的成员,也就是可访问性不一致。
42.值传递和引用传递
值类型:int double char decimal bool enum struct
引用类型: string 数组 自定义类 集合 object 接口
注意:值类型在复制的时候,传递的是这个值得本身,而引用类型在复制的时候,传递的是对这个对象的引用
namespace 值传递和引用传递
{
class Program
{
static void Main(string[] args)
{
//值传递
//int n1 = 10;
//int n2 = n1;
//n2 = 20;
//Console.WriteLine(n1);
//Console.WriteLine(n2);
//Console.ReadKey();
//引用传递
Person P1 = new Person();
P1.Name = "张三";
Person P2 = P1;
P2.Name = "李四";
Console.WriteLine(P1.Name);
Console.ReadKey();
//引用传递
string s1 = "张三";
string s2 = s1;
s2 = "李四";
Console.WriteLine("s1"); //由于string的不可变性,改变其值时,只会在内存开辟新的空间,而不是覆盖
Console.WriteLine("s2");
Console.ReadKey();
}
}
public class Person
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
}
43.sealed密封类
public sealed class Person{} 不能被其他类继承,但可以继承其他类
44.接口(多态)[P154讲]
- 接口的本质是一种规范,只要一个类继承了一个接口,此类就必须实现这个接口中的所有成员。
程序实现:public interface I…able(以I开头,able结尾,表示具备某种能力)
1)接口的一些特点:
(1)接口中的成员不允许添加访问修饰符,默认就是public;而类中不添加,默认是private
(2)接口中可以写入方法、自动属性、索引器、事件(本质都是方法);不允许写具有方法体的函数,也不能包含字段和构造函数。总结以上观点来看,接口中本质上只能写入方法
程序验证:
public interface Iflyable
{
void fly();//实现飞的功能
string TestOne();//方法可以有返回值
void TestTwo()
{
Console.WriteLine("接口成员不能有定义");//程序报错,不允许有方法体
}
string _TestThree;//接口不允许写字段
int TestFour//自动属性:程序默认添加一个该属性对应的私有字段:<Age>k_BackingFiled:private int32(反编译的结果)
{
get;
set;
}
}
(3) 接口不允许实例化对象,抽象类也不允许(Iflyable fly = new Iflyable();)
(4) 接口和接口之间可以继承,并且可以多继承;而类与类之间不可以继承,因为类具有单根性
(5) 接口不能继承一个类,而类可以继承接口(接口只能继承于接口);一个类可以同时继承一个类并且实现(继承)多个接口,如果一个子类(Student)同时继承父类(Person)和实现某种(某些)接口(Iflyable),在语法上必须父类写在接口的前面
程序举例:public class Student : Person, Iflyable { }
2)显示实现接口
程序举例:
namespace 显示实现接口
{
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("鸟会飞");
}
void Iflyable.fly()
{
Console.WriteLine("我是接口的飞功能");
}
}
public interface Iflyable
{
void fly();
}
}