C#基础(完结)
一、复杂数据类型
(一)枚举
它是一个被命名的整型常量的集合,一般用来表示状态、类型等等。
1、申明枚举
命名规范 枚举名 以E或者E_开头
声明在namespace语句块中(常用),也可以在class语句中,struct语句块中
注意:枚举不能在函数语句块中申明
enum E_自定义枚举名{
自定义枚举项名1,
自定义枚举项名2,
自定义枚举项名3,
}
2、申明枚举变量
自定义的枚举类型 变量名 = 默认值;(自定义枚举类型.枚举项)
E_PlayerType playerType = E_PlayerType.Main;
if(playType == E_PlayerType.Main){
Console.WriteLine("主玩家逻辑");
}else if(E_PlayType == E_PlayType.Other){
Console.WriteLine("其它玩家逻辑");
}
3、枚举的类型转换
枚举和int互转
枚举可以直接强转成int,int转枚举可以直接赋值
//枚举转int
int i = (int)playType;
//int转枚举
playType = 0;
枚举和string互转
Parse后 第一个参数:你要转为的是哪个枚举类型 第二个参数:用于转换的对应枚举项的字符串
转换完成后 是一个通用的类型 我们需要用括号强转成我们想要的目标枚举类型
//枚举转string
string str = playType.ToString();
//string转枚举
playType = (E_PlayType)Enum.Parse(typeof(E_PlayerType),"Other");
4、枚举的作用
在游戏开发中,对象很多时候 会有很多的状态
比如一个玩家 有一个动作状态 我们需要用一个变量或标识 来表示当前玩家处于的是哪种状态
综合考虑 可能会使用int来表示他的状态
1行走 2待机 3跑步 4跳跃 ......等等
枚举可以帮助我们 清晰的分清楚状态的含义(提升代码的可读性)
5、枚举练习题
定义QQ状态枚举,并提示用户选择一个在线状态,我们接受输入数字,并将其转换成枚举类型
using System;
namespace Systeam01
{
/// <summary>
/// QQ状态枚举
/// </summary>
enum E_QQType
{
/// <summary>
/// QQ状态
/// </summary>
Online,
Leave,
Busy,
Invisible,
}
class Program
{
static void Main(String[] args)
{
Console.WriteLine("请输入QQ状态(0在线,1离开,2忙,3隐身)");
//ReadLine接收字符串类型,需要强转成int类型
int type = int.Parse(Console.ReadLine());
//int类型强转枚举类型
E_QQType qqType = (E_QQType)type;
Console.WriteLine(qqType);
}
}
}
千问给出代优码化
int.Parse 会抛出异常(如输入非数字字符串)。更安全的做法是使用 int.TryParse:
Console.WriteLine("请输入QQ状态(0在线,1离开,2忙,3隐身)");
// 定义一个变量来接收转换后的结果
int type;
// 尝试转换:如果用户输入的是数字,返回 true 并赋值给 type;否则返回 false
if (int.TryParse(Console.ReadLine(), out type))
{
// 转换成功,进行强转枚举
E_QQType qqType = (E_QQType)type;
Console.WriteLine("你选择的状态是:" + qqType);
}
else
{
// 转换失败,给用户一个友好的提示
Console.WriteLine("输入格式错误,请输入数字!");
}
(二)数组
数组是存储一组相同类型数据的集合
1、数组的申明
变量类型[ ] 数组名;
变量类型[ ]数组名 = new 变量类型[数组长度];
变量类型[ ]数组名 = new 变量类型[数组长度]{内容1,2,3,......};
变量类型[ ]数组名 = new 变量类型[ ]{内容1,2,3,......};
变量类型[ ]数组名 = {内容1,2,3,......};
2、数组的使用
数组长度:数组变量名.length
获取数组中的元素:数组中的下标和索引是从0开始的,范围时0~length - 1;
修改数组中的元素:直接赋值,注意数据类型
遍历数组:用for循环遍历
增加数组中的元素: 数组初始化后是不能够直接添加新元素的,需要新建一个数组,将原数组内容赋值给新数组。
删除数组的元素:原理同上。
查找数组中的元素:遍历数组,用if循环判断,最后返回i。
(三)二维数组
使用两个下标(索引) 行标和列标来确定元素的数组
1、二维数组的申明
变量类型[ , ] 二维数组名;
变量类型[ , ] 二维数组名 = new 变量类型 [ 行,列 ];
变量类型[ , ] 二维数组名 = new 变量类型 [ 行,列 ]{{0行内容1,2,3,...},{1行内容1,2,3,...},{2行内容1,2,3,...},...};
变量类型[ , ] 二维数组名 = new 变量类型 [ , ]{{0行内容1,2,3,...},{1行内容1,2,3,...},{2行内容1,2,3,...},...};
变量类型[ , ] 二维数组名 = {{0行内容1,2,3,...},{1行内容1,2,3,...},{2行内容1,2,3,...},...};
2、二维数组的使用
二维数组的长度:
//得到多少行
Console.WriteLine(array.GetLength(0));
//得到多少列
Console.WriteLine(array.GetLength(1));
获得二维数组的元素:第一个元素索引是0,最后一个元素索引是长度-1;
修改元素:索引位置后直接赋值;
遍历二维数组:
for(int i = 0; i < array.GetLength(0); i++){
for(int = j; j < array.GetLength(1);j++){
Console.WriteLine(array[i,j]);
}
}
增加或删除数组中的元素:把数据搬进新数组;
查找数组中的元素:遍历;
(四)交错数组
交错数组是数组的数组,每个维度的数量可以不同
1、交错数组的声明
变量类型[ ] [ ] 交错数组名;
变量类型[ ] [ ] 交错数组名 = new 变量类型[行数][ ];
变量类型[ ] [ ] 交错数组名 = new 变量类型[行数][ ]{一维数组1,一维数组2,......};(内部一维数组的数据类型要和交错数组数据类型相同)
变量类型[ ] [ ] 交错数组名 = new 变量类型[ ][ ]{一维数组1,一维数组2,......};
变量类型[ ] [ ] 交错数组名 = {一维数组1,一维数组2,......};
2、交错数组的使用
数组的长度:
//行数
Console.WriteLine(array.GetLength(0));
//某一行的列数
Console.Write(array[0].Length);
获取交错数组中的元素:
Console.WriteLine(array[i][j]);
修改元素:索引后赋值
变量交错数组
for(int i = 0; i < GetLength(0); i++){
for(j = 0; j < array[i].Length; j++){
Console.WriteLine(array[i][j] + " ");
}
Console.WriteLine();
}
二、值类型和引用类型
(一)值类型和引用类型的区别
引用类型:string, 数组, 类;
值类型:除去string和数组之外学习到的其它数据类型(int,float,...), 结构体;
1、使用上的区别
值类型 在相互赋值时 把内容拷贝给了对方 它变我不变
引用类型 的相互赋值 时 让两者指向同一个值 它变我也变
2、为什么会有区别
值类型 和 引用类型 存储的内存区域是不同的 存储方式是不同的
值类型存储在 栈空间 —— 系统分配,自动回收,小而快
引用类型 存储在 堆空间 —— 手动申请和释放,大而慢
(二)特殊引用类型string
string非常特殊 它具备 值类型的特征 它变我不变
string str1 = "123";
string str2 = str1;
str2 = "321";
Console.WriteLine(str1);
Console.WriteLine(str2);
//输出结果:
123
321
string重新赋值时 会在堆中重新分配空间
缺点:频繁的改变string 重新赋值 会产生 内存垃圾
优化替代方案 在C#核心中进行讲解
三、函数
(一)函数基础
本质是一块具有名称的代码块
可以使用函数(方法)的名称来执行该代码块
函数(方法)是封装代码块进行重复利用的一种机制
1、函数(方法)的主要作用:
封装方法
提升代码复用率
抽象行为
2、函数写在哪里
class或struct语句块中
3、基本语法
static 返回类型 函数名(参数类型 参数名1,参数名2,......)
{
函数的代码逻辑;
函数的代码逻辑;
函数的代码逻辑;
......
return 返回值;(如果有返回类型才返回)
}
(1)关于static 不是必须的 在没有学习类和结构体之前 都是必须写的
(2)关于返回类型
引出一个新的关键字 void(表示没有返回值)
可以写任意的变量类型 14种变量类型 + 复杂数据类型(数组,枚举,结构体,类class)
(3)关于函数名 使用帕斯卡命名法命名 MyName
(4)参数不是必须的 可以有0~n个参数 参数类型也是可以是任意类型的 14种变量类型 + 复杂数据类型(数组,枚举,结构体,类class)
参数命名 驼峰命名法 myName
(5)当返回值类型不为void时 必须通过新的关键词 return返回对应类型的内容(注意:即使是void也可以选择性使用return)
4、实际应用
(1)无参无返回值函数
static void SayHello()
{
Console.WriteLine("Hello World!");
}
(2)有参无返回值
static void SayYourName(string name)
{
Console.WriteLine("你的名字是:{0}",name};
//return省略了
}
(3)无参有返回值
Static string WhatYourName()
{
return "ZCloud";
}
(4)有参有返回值
static string Sum(int a,int b)
{
return a + b;
}
(5)有参有多返回值
传入两个数 然后计算两个数的和 以及它们的平均数 得出计算结果返回出来
函数的返回值 一定是一个类型 只能是一个内容
static int[] Clac(int a,int b)
{
int sum = a + b;
int avg = sum / 2;
return new int[] {sum,avg};
}
5、关于return
即使函数没有返回值,我们也可以使用return。
return可以直接不执行之后的代码,直接返回到函数外部。
(二)ref和out
它们可以解决函数内部改变外部传输内容 里面变了外面也要变
1、ref的使用
class Program
{
static void ChangeValueRef(ref int value)
{
value = 3;
}
static void Main(string[] args)
{
int a = 1;
ChangeValueRef(ref a);
Console.WriteLine(a);
}
}
//输出结果:
3
2、out的使用
class Program
{
static void ChangeValueOut(out int value)
{
value = 3;
}
static void Main(string[] args)
{
int a;
ChangeValueOut(out a);
Console.WriteLine(a);
}
}
//输出结果:
3
3、ref和out的区别
ref传入的变量必须初始化 out不用
class Program
{
static void ChangeValueRef(ref int value)
{
value = 3;
}
static void Main(string[] args)
{
int a;
ChangeValueRef(ref a);
Console.WriteLine(a);
}
}
//会在ref a处报错,提示:使用了未赋值的局部变量'a';
out传入的变量必须在内部赋值 ref不用
class Program
{
static void ChangeValueOut(out int value)
{
}
static void Main(string[] args)
{
int a = 1;
ChangeValueOut(out a);
Console.WriteLine(a);
}
}
//会在函数声明ChangeValueOut处报错,提示:控制离开当前方法之前必须对out参数'value'赋值
(三)变长参数和参数默认值
1、变长参数关键字 params
params int[ ]意味着可以传入n个int参数 n可以等于0 传入的参数会存在arr数组中
注意:
params关键字后面必为数组
数组的类型可以是任意类型
函数参数可以有 别的参数和params关键字修饰的参数
函数参数中只能最多出现一个params关键字 并且一定是在最后一组参数 前面可以有n个其它参数
2、参数默认值
有参数默认值的参数 一般称为可选参数
作用是 当调用函数时可以不传入参数 不传就会使用默认值作为参数的值
static void Speak(string str = "我没什么话可说")
{
Console.WriteLine(str);
}
注意:
支持多参数默认值 每个参数都可以有默认值
如果要混用 可选参数 必须写在普通参数后面
(四)函数重载
1、基本概念
重载概念:
在同一语句块中(class或struct)中
函数(方法)名相同
参数数量不同
或者
参数的数量相同,但参数的类型或顺序不同
作用:
命名一组功能相似的函数,减少命名空间的污染
提升程序的可读性
1、实例
注意:
重载和返回值类型无关,只和参数类型,个数,顺序有关
调用时 程序会自己根据传入的参数类型判断使用哪一个重载
static int Calcsum(int a,int b)
{
return a + b;
}
参数数量不同
static int Calcsum(int a,int b,int c)
{
return a + b +c;
}
数量相同,类型不同
static float Calcsum(float a,float b)
{
return a + b;
}
数量相同,顺序不同
static float Calcsum(int a,float b)
{
return a + b;
}
static float Calcsum(float a,int b)
{
return a + b;
}
使用ref和out算是一种新的参数类型,但是ref和out之间算是同一种类型
(五)递归函数
让函数自己调用自己
1、一个正确的递归函数:
必须有结束调用的条件
用于条件判断的这个条件 必须改变 能够达到停止的目的
2、实例:
用递归函数打印出0~10
static void Fun(int a)
{
//第四步:结束条件
if(a > 10)
{
return;
}
//第二步:完成要求 打印
Console.WriteLine(a);
//第三步:完成一个 递归的变化 作为我们的条件判断
++a;
//第一步:构造一个递归
Fun(a);
}
四、结构体
1、基本概念
结构体 是一种自定义变量类型 类似枚举需要自己定义
它是数据和函数的集合
在结构体中 可以申明各种变量和方法
作用:用来表现存在关系的数据集合 比如用结构体表现学生 动物 人类等等
2、基本语法
结构体一般写在namespace语句块中
结构体关键字 struct
struct 自定义结构体名
{
//第一部分
//变量
//第二部分
//构造函数(可选)
//第三部分
//函数
}
注意:结构体名字 规范是 帕斯卡命名法
3、实例
表现学生数据的结构体
struct Student
{
//变量
//年龄
public int age;
//性别
public bool sex;
//学号
public int number;
//姓名
public string name;
//构造函数
//函数方法
//表现这个数据结构的行为
public void Speak()
{
//函数中可以直接使用结构体内部申明的变量
Console.WriteLine("我的名字是{0},我今年{1}岁",name,age);
}
}
注意:结构体申明的变量 不能直接初始化
变量类型 可以写任意类型 包括结构体 但是 不能是自己的结构体
在结构体中的方法 目前不需要加static关键字
函数中可以使用结构体内部申明的变量
4、访问修饰符
修饰结构体中变量和方法 是否能被外部使用
public 公共的 可以被外部访问
private 私有的 只能在内部使用
不写默认为 private
5、结构体的使用
static void Main(string[] args)
{
Student s1;
s1.age = 16;
s1.sex = false;
s1.number = 1;
s1.name = "zcloud";
s1.Speak();
}
6、结构体的构造函数
基本概念:
没有返回值
函数名必须和结构体名相同
必须有参数
如果申明了构造函数 那么必须在其中对所有变量数据初始化
struct Student
{
//变量
//年龄
public int age;
//性别
public bool sex;
//学号
public int number;
//姓名
public string name;
//构造函数
public Student(int age, bool sex, int number, string name)
{
//新的关键字 this
//代表自己
this.age = age;
this.sex = sex;
this.number = number;
this.name = name;
}
//函数方法
//表现这个数据结构的行为
public void Speak()
{
//函数中可以直接使用结构体内部申明的变量
Console.WriteLine("我的名字是{0},我今年{1}岁",name,age);
}
}
构造函数一般是方便外部初始化使用
static void Main(string[] args)
{
Student s2 = new Student(18,true,2,"zcloud");
s2.Speak();
}
五、排序初探
(一)冒泡排序
1、基本原理
两两相邻
不停比较
不停交换
比较n轮
2、代码实现
for(int m = 0; m < arr.Length; m++)
{
for(int n = 0; n < arr.Length - m - 1; n++)
{
if(arr[n] > arr[n + 1])
{
int temp = arr[n];
arr[n] = arr[n + 1];
arr[n + 1] = temp;
}
}
}
(二)选择排序
1、基本原理
建立中间商
依次比较
找出极值(最大或最小)
放入目标位置
比较n轮
2、代码实现
//第五步 比较n轮
for(int m = 0; m < arr.Length; m++)
{
//第一步 申明一个中间商 来记录索引
//每一轮开始 默认第一个都是极值
int index = 0;
//第二步 依次比较
for(int n = 1; n < arr.Length; n++)
{
//第三步 找出极值(最大值)
if(arr[index] < arr[n])
{
index = n;
}
}
//第四步 放入目标位置
//Length - 1 - 轮数
//如果当前极值所在就是目标位置 那就没有必要换
if(index != arr.Length - 1 - m)
{
int temp = arr[index];
arr[index] = arr[arr.Length - 1 - m];
arr[arr.Length - 1 - m] = temp;
}
}
更多推荐
所有评论(0)