Break 、Continue 和ReadOnly、Const和Ref和Out params
之前对于Break和Continue;ReadOnly和Const;ref和out,params之类的基础东东学习过,但是一直没有仔细去研究到底是怎么一回事儿,最近在开发中用到了,仔细来做个总结:
1、Break和Continue
break是跳出整个循环体,不再执行本循环,
continue是结束单次循环,继续下一次循环
1 #region Break测试 2 3 Console.WriteLine("========Break========"); 4 int x = 0; 5 while (x++ < 20) 6 { 7 if (x == 3) 8 { 9 break; 10 } 11 Console.WriteLine("{0}\n",x); 12 } 13 #endregion
1 #region Continue测试 2 Console.WriteLine("========Continue========"); 3 int k = 0; 4 while (k++ < 10) 5 { 6 if (k == 3) 7 { 8 continue; 9 } 10 Console.WriteLine("{0}\n",k); 11 } 12 #endregion
2、ReadOnly和Const
1. const修饰的常量在声明的时候必须初始化;readonly修饰的常量则可以延迟到构造函数初始化
2. const修饰的常量在编译期间就被解析,即常量值被替换成初始化的值(编译时常量);readonly修饰的常量则延迟到运行的时候(运行时常量)
3. 此外,Const常量既可以声明在类中也可以在函数体内,但是Static ReadOnly常量只能声明在类中。
1 #region ReadOnly 2 static readonly int A = B * 10; 3 static readonly int B = 10; 4 5 const int j = k * 10; 6 const int k = 10; 7 8 static void Main(string[] args) 9 { 10 Console.WriteLine("===Readonly输出的值是:==="); 11 Console.WriteLine("A is {0}.B is {1}", A, B); 12 Console.WriteLine("===Const输出的值是:==="); 13 Console.WriteLine("j is {0}.k is {1}", j, k); 14 Console.ReadKey(); 15 } 16 #endregion
3、ref 和 out,params
问题的引出:
现需要通过一个叫Swap的方法交换a,b两个变量的值。交换前a=1,b=2,断言:交换后a=2,b=1
现编码如下:
1 1class Program 2 2 { 3 3 static void Main(string[] args) 4 4 { 5 5 int a = 1; 6 6 int b = 2; 7 7 Console.WriteLine("交换前\ta={0}\tb={1}\t",a,b); 8 8 Swap(a,b); 9 9 Console.WriteLine("交换后\ta={0}\tb={1}\t",a,b); 10 10 Console.Read(); 11 11 } 12 12 //交换a,b两个变量的值 13 13 private static void Swap(int a,int b) 14 14 { 15 15 int temp = a; 16 16 a = b; 17 17 b = temp; 18 18 Console.WriteLine("方法内\ta={0}\tb={1}\t",a,b); 19 19 } 20 20 }
运行结果:
交换前 a = 1 b = 2
方法内 a = 2 b = 1
交换后 a = 1 b = 2
并未达到我们的需求!
原因分析:int类型为值类型,它存在于线程的堆栈中。当调用Swap(a,b)方法时,相当于把a,b的值(即1,2)拷贝一份,然后在方法内交换这两个值。交换完后,a还是原来的a,b还是原来的b。这就是C#中按值传递的原理,传递的是变量所对应数据的一个拷贝,而非引用。
修改代码如下即可实现我们想要的结果:
1 class Program 2 2 { 3 3 static void Main(string[] args) 4 4 { 5 5 int a = 1; 6 6 int b = 2; 7 7 Console.WriteLine("交换前\ta={0}\tb={1}\t",a,b); 8 8 Swap(ref a,ref b); 9 9 Console.WriteLine("交换后\ta={0}\tb={1}\t",a,b); 10 10 Console.Read(); 11 11 } 12 12 //交换a,b两个变量的值 13 13 private static void Swap(ref int a, ref int b) 14 14 { 15 15 int temp = a; 16 16 a = b; 17 17 b = temp; 18 18 Console.WriteLine("方法内\ta={0}\tb={1}\t",a,b); 19 19 } 20 20 }
1 关于重载
原则:有out|ref关键字的方法可以与无out和ref关键字的方法构成重载;但如想在out和ref间重载,编译器将提示:不能定义仅在ref和out的上的方法重载
2 关于调用前初始值
原则:ref作为参数的函数在调用前,实参必须赋初始值。否则编译器将提示:使用了未赋值的局部变量;
out作为参数的函数在调用前,实参可以不赋初始值。
3 关于在函数内,引入的参数初始值问题
原则:在被调用函数内,out引入的参数在返回前至少赋值一次,否则编译器将提示:使用了未赋值的out参数;
在被调用函数内,ref引入的参数在返回前不必为其赋初值
总结: C#中的ref和out提供了值类型按引用进行传递的解决方案,当然引用类型也可以用ref和out修饰,但这样已经失去了意义。因为引用数据类型本来就是传递的引用本身而非值的拷贝。ref和 out关键字将告诉编译器,现在传递的是参数的地址而不是参数本身,这和引用类型默认的传递方式是一样的。同时,编译器不允许out和ref之间构成重载,又充分说明out和ref的区别仅是编译 器角度的,他们生成的IL代码是一样的。有人或许疑问,和我刚开始学习的时候一样的疑惑:值类型在托管堆中不会分配内存,为什么可以按地址进行传递呢?值类型虽然活在线程的堆栈 中,它本身代表的就是数据本身(而区别于引用数据类型本身不代表数据而是指向一个内存引用),但是值类型也有它自己的地址,即指针,现在用ref和out修饰后,传递的就是这个指针,所以可 以实现修改后a,b的值真正的交换。这就是ref和out给我们带来的好处。