可以通过某些特定的控制语句来控制代码的程序执行结构
顺序执行语句
分支流程控制 ——> 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);方法就是一个功能的集合,可以把程序中某段具有特殊功能的代码提取出来。
//引用命名空间 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)(形参列表){} { //方法体 }语法
[访问权限修饰符] [其他修饰符] 返回值类型 方法名 ([形参列表]) {
//(加[]表示可有可无)
//方法体
}
//方法声明 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.方法名是一个标识符,遵循大驼峰命名法
一个方法体中的代码如果需要被执行,那么需要调用这个方法
语法:方法名();
static void Main(string[] args) { //如何调用一个方法? MyFirstMethord(); }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 }什么是返回值
返回值:就是一个方法执行的结果。
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; }在一个类中,有多个方法满足以下几个条件,那么这些方法之间是重载关系:
1、方法名相同
2、参数不同(数量不同,类型不同,类型顺序不同)
3、跟返回值,返回权限,权限状态没有关系,
如何区分调用不同的方法:通过实参来区分。
作用:简化代码
递归就是指方法循环调用,合理使用递归可以很方便的做一些事情。
递归使用到后面会越来越慢。如果没有出栈的机会最后会导致栈溢出
使用递归注意事项:一定要留有出栈的时机(使用递归找出口)
用法:
//递归(递归相当于变相的循环,也容易造成死循环,使用时必须留有出口) 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是用来修饰参数的:
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用来修饰参数的:
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默认为赋值状态 }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.一个方法中的代码如果执行完毕,那么这个方法自动出栈。