复习:J&C:面向过程:更严格语法 / 作用域 / 多维数组 / enum / 方法重载
有时候为了书写方便,我们还可以先直接“调用”方法,然后再利用VS的Show potential fixes(Alt+Enter)功能自动生成方法
VS的这个功能非常有用!除了可以生成方法,还可以生成类、字段……,添加using、引用……等等,几乎无所不能,超级利器!
在方法声明的时候,在方法上输入三个斜杠(///),VS就会自动生成方法注释。
/// <summary> /// 找到最低分数 /// </summary> /// <param name="scores">学生成绩</param> /// <param name="first">如果有重复,是否取第一个最低分数即可</param> /// <returns>最低分数</returns> static double min(double[] scores, bool first) { return -1; }我们可以由此填入:
这样,当我们调用方法的时候,就可以得到VS的智能提示:
普通注释也可以使用3个反斜杠(///)。
严格来讲这应该就是行注释(//)的延伸,它不属于C#语法,仅作用于VisualStudio。和//的区别就是:
///引导的注释内容在换行时会自动添加///头(演示),///第一行注释 ///换行自动生成"///" ///
这时候我们将其称之为:多行注释。
我们可以在方法调用处使用快捷键F12转到定义,比如:
public static void WriteLine(string value);
因为这些方法都是通过已编译的dll文件提供的,所以我们只能看到方法的签名(具体原因后文解释)。如果是我们自己写的方法,F12是可以看到方法体的。
到目前为止,其实我们已经接触过一下.NET自带的类库方法了:(复习:Main()、Console.Write/ReadLine(),类型转换函数)
可以点出很多数学运算方法,比如取三角函数(Sin()/Cos())、取绝对值(Ab()),但最常用的是:
Console.WriteLine("Ceiling:" + Math.Ceiling(19.8)); Console.WriteLine("Floor:" + Math.Floor(19.8));注意返回的仍然是double类型而不是int类型,@想一想@:为什么?
Console.WriteLine(double.MaxValue - int.MaxValue);
Console.WriteLine("Round:" + Math.Round(19.875, 2));
有时候我们需要模拟抽签摇色子,取随机数,这就需要用到Random:
Console.WriteLine(new Random().Next());
还可以指定随机数的生成范围(最大值/最小值)
Console.WriteLine(new Random().Next(1000)); Console.WriteLine(new Random().Next(100, 1000));
#体会#:方法重载的使用
在某些电脑上运行:
for (int i = 0; i < 10; i++) { Console.WriteLine(new Random().Next(100)); }
可能得到重复的随机值:
///23 ///23 ///23 ///65 ///65 ///65 ///65 ///12
这是怎么回事呢?
因为计算机是不可能真正随机的,随机数不是“凭空产生”的,计算机不会摇色子,随机数是依据特定的输入(seed),采用固定的算法(源代码)计算出来的。
我们可以在生成Random对象时指定这个seed试试:
for (int i = 0; i < 10; i++) { Console.WriteLine(new Random(986).Next(100)); }
演示:这时候得到的“随机数”一点都不随机了,这被称之为伪随机。
如果不指定seed,.NET用当前时间做seed。所以,如果随机数是在同一时间“点”(CPU时钟周期内)生成的,那他们的值一定是相同的。
解决的办法是,在循环外部生成Random对象,在循环体内调用同一Random对象的Next()方法
Random random = new Random(); for (int i = 0; i < 10; i++) { Console.WriteLine(random.Next(100));
因为.NET会在new Random() 的时候就准备好一系列不重复的随机数,类似于:[28, 23, 17, 5, 92, 88, ....],然后每次调用Next()方法,就给其下一个!
好吧,我承认,这是一个非常蹩脚的翻译。我们有时候又将其称之为“箭头方法”
简单的来说,如果方法体只有一条语句的话,从C# 6.0开始,就可以使用新的写法:
static int Add(int a, int b) => a + b;
上述语句等于:
static int Add(int a, int b) { return a + b; }
也可以是没有返回值的方法:
static void Add(int a, int b) => Console.WriteLine(a + b);
PS:这种写法还可以应用于属性、索引器、构造函数等……
C#中可以传递变量本身,这就需要用ref或out标记:
复习:值类型参数传递
然后,修改方法参数声明:
static void grow(ref int age) { age++; }
注意调用的时候也要添加ref关键字
int a = 18; grow(ref a);
@想一想@:这时候age的值是多少?
ref也可以修饰引用类型变量(但不推荐)。演示区别:
static void grow(/*ref*/ Student student) { //有无这行代码的差别 //student = new Student(); student.Age++; }
引用传递会将变量wx直接传递给grow()方法,也就是说,student参数就是wx本身,那么在方法中新new出来的对象地址也就直接赋值给了wx,所以现在wx.age就是0了(字段的默认值),++之后就变成了1。
ref的经典例子是:交换两个变量的值
static void swap(ref int a, ref int b) { int temp = a; a = b; b = temp; }
out和ref的效果类似,都是属于引用传递。
但是,使用out不能将该参数的值传入方法中:
static void grow(out int age) { //age++; //会报错:不能使用out参数的原有值 age = 20; }
所以传入的out参数根本不需要事先被赋值:
int age; //输出参数:传入一个未赋值(赋值也没用) grow(out age); //在C#的后期版本中可以直接简写为(推荐): //grow(out int age);
out常用于一个方法需要多个返回的时候(即:获取除了返回值以外的结果),比如类库中的TryParse:
//如果能够Parse,方法返回值为true,input为转换结果 //否则,方法返回值为false,input为默认值0 if (double.TryParse(Console.ReadLine(), out double input)) { input += 10; } else { Console.WriteLine("输入错误……"); }
常见面试题:ref和out的区别
牢记:C#中除非方法参数做了标记(ref/out),一律都是值传递!!!
复习:方法重载的作用
实现默认参数值的效果。
C#更进一步,为这种实现推出语法糖:可选参数
在参数后面加一个等号,赋一个默认值:
static void exam(int basic = 100)
在调用的时候,该参数:
exam(50);
exam();
所有可选参数都要放在必须(required)参数后面。这样是不行的:
static void exam(int basic = 100, bool passed)
可选参数的默认值必须是“编译时”确定的,比如:
const int basic = 60; static void exam(int bonus = basic){}
static void exam(int bonus = default(int)){}
static void exam(DateTime bonus = new DateTime()){}
可以引入参数名。比如方法是这样声明的:
static void exam(int basic = 60, int bonus = 20){}
这样的调用,首先“可读性”就不高,86究竟是指basic还是bonus?(按语法规则是basic,从左往右排……)
exam(86);
然后,如果我想将86指定给bonus,basic使用默认值呢,怎么办?
exam(bonus: 86);
可选和非可选参数之间不能构成“重载”……
static void getMax(params int[] array)这样,当我们调用该方法的时候,就可以(不是必须只能)直接传递数组元素了:
getMax(3, 9, 18, 23); //等效于: getMax(new int[] { 3, 9, 18, 23 });
多快好省!前端后端,线上线下,名师精讲
更多了解 加: