一、流程控制
可以通过某些特定的控制语句来控制代码的程序执行结构
顺序结构
顺序执行语句
分支结构
分支流程控制 ——> if-else,switch-case
if-else
注意点:
1、可以只有if没有else,反之则不行。
2、如果{}中只有一句执行语句时,{}可以省略。
eg:
//if-else语句
bool condition = true;
//if里面写bool类型的条件
if(condition){
//如果if为true,执行
Console.WriteLine("!");
}
else{
//如果if为false,执行
Console.WriteLine("!");
}
//实例
int score = 123;
if (score < 0)
{
Console.WriteLine("负成绩");
}else if (score >= 0 & score <= 100)
{
Console.WriteLine("合法成绩");
}
else
{
Console.WriteLine("作弊了");
}
swith-case
注意点:
1、case捕获的值需要和witch的变量类型匹配
2、不允许出现多个case的值是一样的
3、关键字break
a、break有跳出作用,具有穿透性
b、在C#中,如果一个case后面没有语句,那么可以不加break保留穿透性
c、如果一个case后面有语句,那么break必须添加
eg:
//switch-case语句
int coondition = 1;
//小括号里面可以写任意的数据类型
//选择一个任意类型的变量,捕获特定的值,如果变量的值和捕获到的值是一样的,那么执行case语句后的代码。
switch (coondition)
{
case 0:
Console.WriteLine("condition的值是0");
break;
case 1:
Console.WriteLine("condition的值是1");
break;
default: //用来捕获上面的case都没有满足的情况
Console.WriteLine("以上都不对");
break;
}
//实例
int season = 3;
switch (season)
{
case 1:
Console.WriteLine("春困");
break;
case 2:
Console.WriteLine("夏打盹");
break;
case 3:
Console.WriteLine("秋乏");
break;
case 4:
Console.WriteLine("冬眠");
break;
// 测试
//'a'不报错是因为字符型可以自动转换为整型(隐式转换)
case 'a':
Console.WriteLine("hello world");
break;
//报错是因为重复出现1
case 1:
Console.WriteLine("hello world");
break;
//这里的'1'也是字符型,所以不报错
case '1':
Console.WriteLine("hello world");
break;
//报错:1.1f不能隐式转换为整型,可以加强制转换为整型 case (int)1.1f:,但没必要,因为强制转换后就是1
case 1.1f:
Console.WriteLine("hello world");
break;
default:
Console.WriteLine("适合学习的季节");
break;
}
//关键字break
/*
* break有跳出作用,具有穿透性,
* 在C#中,如果一个case后面没有语句,那么可以不加break保留穿透性
* 如果一个case后面有语句,那么break必须添加
*/
int month = 3;
switch (month)
{
case 1:
Console.WriteLine("31天");
break;
case 2:
case 3://如果month=3,因为穿透性,最终结果为:31天
case 4:
case 5:
Console.WriteLine("31天");
break;
}
//打印月份对应天数
int month = 2;
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:
Console.WriteLine("28天");
break;
}
循环结构
循环流程控制 ——> for,while,do-while
for
注意点:
a、关键字break:结束循环(就算条件依然满足,也要终止循环)
b、关键字continue:跳过本次循环,进入下一循环
//for循环语句
//循环条件
/*
* for(循环的起点;循环的条件;循环的步长){
* 如果循环的条件满足,那么这里的代码会重复执行
* 这部分代码被称作——循环体}
*/
//小明从0跑到100米
for (int meter = 0; meter <= 100; meter++)
{
Console.WriteLine("小明跑了" + meter + "米");
}
/*
* 循环中的每部分的执行顺序:
* 1:int num = 2; 循环的起点
* 2:num <= 100; 循环的条件
* 如果条件成立:
* 3:循环体
* 4:循环步长
* 2:再次回到第二步判断条件
* 如果不成立:
* 3:结束循环
*/
//关键字break:在循环中表示结束一个循环
for (int i = 0; i <= 5; i++)
{
if (i == 5)
{//就算是循环条件依然满足,也需要结束一个循环
break;
}
Console.WriteLine(i);//结果为0~4,后面的不再打印
}
//关键字continue:跳过本次循环,进入下一次循环
for (int j = 0; j <= 10; j++)
{
if (j == 5)
{//结束本次循环,然后进入下一次的循环
continue;
}
Console.WriteLine(j);//结果为0~10,但是没有5,原因是到等于5时,跳过了本次循环输出,直接到一下次
}
//eg1:循环输出1~100的偶数
//第一种方式
for (int num = 0; num <= 100; num++)
{
if (num % 2 == 0)
{
Console.WriteLine(num);
}
}
//第二种方式
for (int num = 0; num <= 100; num += 2)
{
Console.WriteLine(num + 2);
}
//第三种方式
for (int m = 1; m <= 100; m++)
{
if (m % 2 == 1)
{
continue;
}
Console.WriteLine(m);
}
//eg2:输出1+2+3+……+100的和
int sum = 0;//为例=l储存遍历到的数字的和
for(int num = 1;num <= 100; num++)
{
sum += num;
}
Console.WriteLine(sum);
//死循环
//第一种
for (int i = 0; i < 10; i += 0)//没有步长
{
Console.WriteLine("Hello World");
}
//第二种
for ( ; ; )
{
Console.WriteLine("Hello World");
}
while
注意点:
while小括号中的循环条件为真,执行循环体。
//while循环语句
//语法
//while的小括号中只需要写循环的条件
while (true)
{
//循环体
}
//eg:0到100的和
int i = 1;
int sum = 0;
while (i <= 100)
{
sum += i++;// ==> sum = sum + i;i = i + 1
}
Console.WriteLine(sum);
do-while
注意点:
在do-while语句中要先执行循环体然后才判断条件,不管条件是否成立都必须执行循环体
//do-while循环语句
//语法
do
{
//循环体
} while (循环条件);
while与do-while两者区别
while:先判断循环条件是否满足,然后再决定是否循环
do-while:先执行一次循环体,然后再判断执行条件是否成立,决定是否继续下次循环
二、输出输出
write(输出语句):(与类型转换配合使用)
字符串转其他数据类型
Console.ToInt16() ——> short
Console.ToInt32() ——> int
Console.ToInt64() ——> long
Console.ToSByte() ——> sbyte
Console.ToSingle() ——> float
Console.ToDouble() ——> double
Console.ToDecimal() ——> decimal
Console.ToChar() ——> char
Console.ToBoolean() ——> bool
eg:
//字符串转其他的数据类型(凡是牵扯到字符串的转型都用concert)
String input = Console.ReadLine();//输入:12345
Console.WriteLine(input);//结果为:12345
int number = Convert.ToInt32(input);
Console.WriteLine(number+1);//结果为:12346
补充:Convert转换过程中输入的数值必须是符合类型的 。(int num =Console.ToInt32() 此时若输入3.14,执行后会出现异常:字符串输入类型不符。)
其他数据类型转字串
a、Concert.ToString() ——> string
b、使用字符串连接符+
eg:
//其他数据类型转字符串类型
//方式一
String str = Convert.ToString(3.14);
//方式二
str = 3.14 + "";
read(输入语句):
1.Console.Read():从控制台读取一个字符
int result = Console.Read();
Console.WriteLine(result);
2.Console.ReadKey():等待用户输入任何内容(请输入任意键)
ConsoleKeyInfo info = Console.ReadKey();
3.Console.ReadLine():从控制台读取一行内容(unity中用的多)
String input = Console.ReadLine();
Console.WriteLine(input);
三、方法、函数
1.什么是方法
方法就是一个功能的集合,可以把程序中某段具有特殊功能的代码提取出来。
//引用命名空间
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//namespace命名空间
namespace aVariable
{
class Program //class类 类名
{
//mian主函数:程序是从这里开始执行的
static void Main(string[] args)// 静态修饰符(static) 类型(void) 方法名字(mian)(形参列表){}
{
//方法体
}
2.方法的声明
语法
[访问权限修饰符] [其他修饰符] 返回值类型 方法名 ([形参列表]) {
//(加[]表示可有可无)
//方法体
}
//方法声明
static void MyFirstMethord()
{
for (int line = 1; line <= 9; line++)
{
for (int colum = 1; colum <= line; colum++)
{
Console.Write($"{line}x{colum}={line * colum,-2} ");
}
Console.WriteLine();
}
}
注意点
1.方法与方法是平级的,不允许出现方法嵌套方法
2.方法名是一个标识符,遵循大驼峰命名法
3.方法的使用(调用)
一个方法体中的代码如果需要被执行,那么需要调用这个方法
语法:方法名();
static void Main(string[] args)
{
//如何调用一个方法?
MyFirstMethord();
}
4.参数
a、参数是什么
其实就是一个变量,在调用方法时候,需要给方法中所有的形式参数赋值
static void Main(string[] args)
{
//调用方法时,10给a进行赋值
Test(10);
Test2(10, 1.1f, 3.14, 'a');
Test3();//此时默认a的初始值为10,若此时Test3(100),则a的值为100,新赋值将覆盖原来的值。
Test4(10);//此时10的值赋值给了a
Test5(30, 40);//此时30的值赋值给了a,40的值赋值给了b(按顺序覆盖初始值),并且不能跳过b的值直接赋值给c
}
//a被称作 ——> 形式参数 ——> 形参
//调用方法的时候传的参数被称作 ——> 实际参数 ——> 实参(10)
static void Test(int a)
{
Console.WriteLine(a * a);//结果为:100
}
static void Test2(int a, float b, double c, char d)
{
Console.WriteLine(a);//10
Console.WriteLine(b);//1.1f
Console.WriteLine(c);//3.14
Console.WriteLine(d);//a
}
static void Test3 (int a = 10){
Console.WriteLine(a);//结果为:10
}
static void Test4(int a, int b =1)
{
//形参列表中如果存在没有被初始化的值(int a),那么该值必须放在已被初始化的值之前(int b = 1)。
}
static void Test5(int a, int b = 10, int c = 20)
{
//若有初始值,则按顺序覆盖值
}
b、局部变量和全局变量
变量可以分为局部变量和全局变量
局部变量:书写在方法或者一个代码段内的变量
全局变量:书写在类中,与方法平级的变量
c、变量的作用域
变量作用域:表示一个变量能够被访问的范围。
一般来说,局部变量的作用域为:从声明开始,到声明所在的大括号{}结束。但在for循环中,循环起点定义的变量仅在for循环中生效。
d、交换变量(坑)
static void Main(string[] args)
{
//交换变量
int x = 10, y = 20;
Swap(x, y);
Console.WriteLine($"x = {x},y = {y}");//结果为:x = 10, y = 20
/*
* x和y的值不能交换是因为,在程序执行中Main方法先进行压栈并调用Swap方法,
* 在调用时,只是把x和y的值复制了一份给a和b,其本身x = 10,y = 20并没有改变,
* 当a和b的值在Swap方法中交换完之后,Swap方法也调用执行完成,并且出栈,
* 而此时x,y的值并没有发生改变。
* 因此得到的结果中a,b的值进行了交换;而x,y的值没有进行交换。
*/
}
//交换变量
static void Swap(int a, int b)
{
int c = a;
a = b;
b = c;
Console.WriteLine($"x = {a},y = {b}");//结果为:x = 20, y = 10
}
5.返回值
什么是返回值
返回值:就是一个方法执行的结果。
void
不是数据类型,表示空,没有返回值。
如果一个方法不需要执行结果,这个方法的返回值类型可以设计为void。
return
return具有两层含义
1.将后面的值作为方法的执行结果返回
2.结束方法
注意事项
1.如果一个方法的返回值不是void,那么在这个方法结束执行之前,必须有一个具体的返回值。
2.如果一个方法中有分支,那么必须保证每一个分支上都有返回值。
3.返回值的类型必须和具体的返回值的类型要匹配(可以有隐式转换的存在)。
4.在返回值类型为void的方法中是可以使用return的。
static void Main(string[] args)
{
//返回值
//计算数字和并将结果传给main数
/* int result = Add(10, 20);
Console.WriteLine(result);*/
Console.WriteLine(Add(10,20)); //结果为:30
int result = Add2(10,20);
Console.WriteLine(result);
int result = Add3(10,20);
Console.WriteLine(result);//报错!返回值类型不匹配
Add4();
}
static int Add(int a,int b)
{
int c = a + b;
//return:将后面的值作为方法的执行结果
return c;
//return后面的代码将不再执行
Console.WriteLine("Hello World");
}
static int Add2(int a,int b)
{
if(a > b){
return a - b;//其中一个分支有返回值,其他分支也必须有返回值
}else{
return a + b;
}
}
static int Add3(int a,int b)//会报错是因为返回值类型不能转换
{
return "Hello World";
}
static void Add4()//void型也可使用return,结束方法
{
return;
}
6.方法的重载(OverLoad)
在一个类中,有多个方法满足以下几个条件,那么这些方法之间是重载关系:
1、方法名相同
2、参数不同(数量不同,类型不同,类型顺序不同)
3、跟返回值,返回权限,权限状态没有关系,
如何区分调用不同的方法:通过实参来区分。
作用:简化代码
7.递归
递归就是指方法循环调用,合理使用递归可以很方便的做一些事情。
递归使用到后面会越来越慢。如果没有出栈的机会最后会导致栈溢出
使用递归注意事项:一定要留有出栈的时机(使用递归找出口)
用法:
//递归(递归相当于变相的循环,也容易造成死循环,使用时必须留有出口)
static void Main(string[] args)
{
Show();
}
static void Show()
{
Console.WriteLine("show");
Show();
}
eg:菲波那切数列
static void Main(string[] args)
{
//eg:
//1、打印前三十位的菲波那切数列
for (int index = 1; index <= 30; index++)
{
Console.WriteLine(GetNumberByIndex(index));
}
//2、打印菲波那切数列指定位数字
Console.WriteLine(GetNumberByIndex(Convert.ToInt32
(Console.ReadLine())));
}
//eg:求斐波那切数列的指定位数字
//参数index:要获取的哪一位的数字
//返回值:index对应的数字
static int GetNumberByIndex(int index)
{
//是递归的出口
if (index == 1 || index == 2)
return 1;
//获取前两位的数字的和
return GetNumberByIndex(index - 2) + GetNumberByIndex(index - 1);
}
四、ref/out关键字
ref关键字
ref是用来修饰参数的:
1、如果一个形参用ref来修饰了,那么对应的实参也得用ref来修饰
2、在传参的时候,传递的其实是实参的地址而不是实参的值
eg:两数交换
static void main(String[] args)
{
//ref关键字
//设计一个方法交换两个变量的值
int x = 10, y = 20;
Swap(ref x, ref y);
Console.WriteLine($"x = {x},y = {y}");
}
static void Swap(ref int a, ref int b)
{
//int c = a;
//a = b;
//b = c;
a = a ^ b;
b = a ^ b;//b = a ^ b ^ b;
a = a ^ b;//a = a ^ b ^ a ^ b ^ b;
}
out关键字
out关键字主要作用:out方法里面的值给方法外面的变量去赋值的
out用来修饰参数的:
1、如果一个形参用out来修饰了,那么对应的实参也得用out来修饰
2、out修饰的参数在传参的时候,传递的是实参的地址
static void mian(String[] args)
{
int x = 5;
Change(out x);
Console.WriteLine(x);
}
static void Change(out int a)
{
a = 10;
Console.WriteLine(a);//必须在赋值之后打印,因为加out关键字后,a默认为赋值状态
}
ref与out的区别
1、在方法结束之前必须对out参数必须进行赋值,类似返回值,而ref没有这个要求
2、ref参数可以直接使用,是默认有值的,指向实参的值,而out参数默认是未赋值的状态,不能直接使用
3、如果方法一个接受ref关键字,而另一个接受out关键字,那么方法将不能重载
五、数组操作
排序
选择排序
原理:选择一个下标,然后用这个下标对应的元素依次和后面的每一个元素进行比较
static void main(String[] args){
//数组排序
//1、选择排序
//原理:选择一个下标,然后用这个下标对应的元素依次和后面的每一个元素进行比较
int[] array = { 1, 2, 13, 46, 57, 67, 4 };
Sort01(array);
foreach (int item in array)
{
Console.Write(item + ",");
}
}
//使用选择排序来对一个数组进行升序排序
static void Sort01(int[] array)
{
//外层循环用来遍历需要和后面所有的元素比较的下标
for (int i = 0; i < array.Length - 1; i++)
{
//内层循环的下标依次和外层的下标进行比较
for (int j = i + 1; j < array.Length; j++)
{
//交换的条件
if (array[i] > array[j])
{
//如果前面的元素大就交换元素
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
}
冒泡排序
原理:依次比较两个相邻的元素
//2、冒泡排序
#region
//原理:依次比较两个相邻的元素
int[] array = { 1, 2, 5, 3, 4 };
Sort02(array);
foreach (int item in array)
{
Console.Write(item + ",");
}
//使用冒泡排序对数组进行升序排序
static void Sort02(int[] array)
{
for (int i = 0; i < array.Length; i++)//循环的趟数
{
//每比较完一趟就一定会有一个较大的数被排好所以循环的条件是:j < array.Length - i - 1
for (int j = 0; j < array.Length - i - 1; j++)//每趟循环的次数
{
if (array[j] > array[j + 1])
{
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
查找
顺序查找
从数据的第一个元素开始,依次比较,直到找到目标数据或查找失败。
/*
1.从表中的第一个元素开始,依次与关键字比较。
2.若某个元素匹配关键字,则 查找成功。
3.若查找到最后一个元素还未匹配关键字,则 查找失败。
*/
public class Program {
public static void Main(string[] args) {
int[] array = { 10, 19, 21, 67, 99, 110, 920, 1000 };
Console.WriteLine(SequentialSearch(array, 80));
Console.ReadKey();
}
private static int SequentialSearch(int[] array, int key) {
for (int i = 0; i < array.Length; i++)
if (array[i] == key)
return i;
return -1;
}
}
二分查找
前提条件:数组需要是排序的
原理:每次查找指定范围的中间值
int[] array = { 10, 19, 21, 67, 99, 110, 920, 1000 };
int index = -1;//表示查找到的元素的下标
//确定一个要查找的下标范围
int min = 0;
int max = array.Length - 1;
while (max >= min)//确保数组有元素 循环条件:极限状态下max = min 表示数组只有一个元素
{
int mid = (min + max) / 2;
if (array[mid] == 920)
{
index = mid;
break;
}
else if (array[mid] > 920)
{
max = mid - 1;//查找范围缩小到一半,即范围改变成(0~中间值的前一个数)
}
else
{
min = mid + 1;//查找范围缩小一半,即范围改变成(中间值的后一个数~max)
}
}
onsole.WriteLine(index);
六、栈和队列
在栈和堆上但凡是用来存引用的,地址的都是8字节。(例如:String——引用类型8字节)
1.一个方法如果需要被执行,那么这个方法需要被压到栈中执行。
压栈(push)出栈(pull)
2.一个方法中的代码如果执行完毕,那么这个方法自动出栈。