【C#本质论 五】方法和参数
前面四篇博客主要介绍了一些基本的操作内容,包含之前不了解的冷知识,以及一些新学到的C#较新版本的新知识,做个简单的小结:
- 新知识:元组、tryParse(out可内联)、字符串插值、空合并操作符、空条件操作符、可空修饰符、switch的模式匹配
- 冷知识:同类型单行声明赋值、字符串的不可变、字符串驻留技术、运算符优先级、预编译指令
对于个人来说还是小有帮助的,至少再看到一些奇怪的类似??的符号不会说不认识了。从本篇博客开始,了解下方法的调用和参数,依旧惯例,因为知道方法怎么调,所以还是使用新、老知识的方式。
新学概念
包括一些方法的多种参数形式,传递形式。
引用的方式传递值类型
三大引用类别的参数及返回引用:ref,out,in,返回引用:
1 ,使用ref方式,可以将一个值类型以传引用的方式传递,在IL代码里可以看到,传递的是一个地址(指向值)而非值的拷贝,所以通过这种方式可以改变值,使值类型可以像引用类型一样使用,需要注意的是引用类型传值之前必须赋值!,ref只是为现有变量分配别名,而非创建新变量并将实参的值拷贝给它。
public static void Main()
{
// ...
int first = 5;
int second = 6;
Swap(ref first, ref second);
System.Console.WriteLine(
$@"first = ""{ first }"", second = ""{ second }""");
// ...
}
private static void Swap(ref int x, ref int y)
{
int temp = x;
x = y;
y = temp;
}
} //交换first和second的值:first为6,second为5, 如果是值类型,其不会变
2, 使用out方式,C#7.0开始out类型不用事先声明,用法和ref类似,需要注意的是,out参数在方法里必须被赋值
public static int Main(string[] args)
{
if(args.Length == 0)
{
Console.WriteLine(
"ConvertToPhoneNumber.exe <phrase>");
Console.WriteLine(
"'_' indicates no standard phone button");
return 1;
}
foreach(string word in args)
{
foreach(char character in word)
{
#if !PRECSHARP7 //预编译指令,用来区分环境,只有7.0以上版本才允许使用内联而不用提前声明
if(TryGetPhoneButton(character, out char button))
#else
char button;
if(TryGetPhoneButton(character, out button))
#endif // PRECSHARP7
{
Console.Write(button);
}
else
{
Console.Write('_');
}
}
}
Console.WriteLine();
return 0;
}
3, 使用in方式,不仅可以让值类型以引用的方式传递,而且被调用的方法不能修改值类型。
private void Method(in int number) { }
4,返回引用:7.0版本新增,可以返回对变量的引用,照常理说数组被方法改变后,里面的值就不会变化了,但是返回引用可以运行变量在没有接触到数组的前提下,直接修改数组里的元素,因为它持有这个元素的引用啊
public class Program
{
// Returning a reference
static public ref byte FindFirstRedEyePixel(byte[] image)
{
// Do fancy image detection perhaps with machine learning
for (int counter = 0; counter < image.Length; counter++)
{
if (image[counter] == (byte)ConsoleColor.Red)
{
return ref image[counter];
}
}
throw new InvalidOperationException("No pixels are red.");
}
public static void Main()
{
byte[] image = null;
// Load image
image = new byte[42];
for (int i = 0; i < image.Length; i++)
{
image[i] = (byte)ConsoleColor.Black;
}
image[(new Random()).Next(0, image.Length - 1)] = (byte)ConsoleColor.Red;
// Obtain a reference to the first red pixel
ref byte redPixel = ref FindFirstRedEyePixel(image);
// Update it to be Black
redPixel = (byte)ConsoleColor.Black;
System.Console.WriteLine((ConsoleColor)image[redPixel]); //byte数组里第一个元素颜色被更新为黑色
}
}
使用禁忌:只能返回对字段或数组元素的引用、其它返回引用的属性或方法、作为参数传递给返回引用的方法的引用(image).
参数数组、可空参数及具名参数
三种动态使用方法参数的方式:参数数组、可选参数及具名参数。
1,参数数组支持将数组作为参数传递,有个好处就是,可以充分使用这些参数:
public class Program
{
public static void Main()
{
string fullName;
// ...
// Call Combine() with four parameters
fullName = Combine(
Directory.GetCurrentDirectory(),
"bin", "config", "index.html");
Console.WriteLine(fullName);
// ...
// Call Combine() with only three parameters
fullName = Combine(
Directory.GetParent(Directory.GetCurrentDirectory()).FullName,
"Temp", "index.html");
Console.WriteLine(fullName);
// ...
// Call Combine() with an array
fullName = Combine(
new string[] {
$"C:{Path.DirectorySeparatorChar}", "Data",
"HomeDir", "index.html" });
Console.WriteLine(fullName);
// ...
}
static string Combine(params string[] paths)
{
string result = string.Empty;
foreach(string path in paths)
{
result = System.IO.Path.Combine(result, path); //将一系列参数拼接为一个完整路径
}
return result;
}
}
我感觉比较适合将某一类参数作为数组,聚类效应吧。注意,如果要使用参数数组,要放到参数列表最后一个。
2,可选参数,通常用法是给参数赋默认值,使用可选参数的时候如果不传值就用默认值。注意,如果要使用可空参数,要放到参数列表最后一个,默认值必须是常量或者能在编译时确定的值。
static int DirectoryCountLines(
string directory, string extension = "*.cs")
{
int lineCount = 0;
foreach(string file in
Directory.GetFiles(directory, extension))
{
lineCount += CountLines(file);
}
foreach(string subdirectory in
Directory.GetDirectories(directory))
{
lineCount += DirectoryCountLines(subdirectory);
}
return lineCount;
}
3,具名参数,调用者可利用具名参数显示指定值,但是有限制,如果参数名改变了,代码会报错
public class Program
{
public static void Main()
{
DisplayGreeting(
firstName: "Inigo", lastName: "Montoya");
}
public static void DisplayGreeting(
string firstName,
string middleName = default(string),
string lastName = default(string))
{
// ...
}
}
值得注意的是,可选参数、具名参数、方法重载这些方式可能会导致方法之间无法区分,这时候会优先选择最具体的方法,有个先后顺序:例如对于方法重载,int比long具体,long比double具体等等。还有就是如果两个方法都有相同参数,但一个使用可选参数,则优先使用另一个,因为编译器只使用调用者显式标识的参数。
C#冷知识
1,虽然完全限定的方法名称包括:命名空间.类型名称.方法名称,但是假定要调用的方法与发出调用的方法在同一个命名空间,就没必要指定命名空间,同理要调用的方法与发出调用的方法在同一个类型里 ,不仅没必要指定命名空间,而且没必要指定类型名称。
2,不能在方法中声明与参数同名的局部变量。
3,嵌套命名空间必须显示导入!,例如想使用StringBuilder,虽然添加了using System,但是还是需要有using System.Text,这解释了之前的疑惑。
using System; //不必须
using System.Text;
namespace ClassLibrary1
{
public class Class1
{
private StringBuilder stringBuilder = new StringBuilder();
}
}
4,using指令允许省略命名空间,而using static 允许省略命名空间和类型名称,只需写静态成员名称。可以理解,因为静态方法不允许重复。
5,利用using指令为命名空间或类取一个别名,可以避免冲突(System.Timers和System.Threading下均有Timer类),例如这里的Timer类型就用CountDownTimerd代替。
namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter05.Listing05_10
{
using System;
using System.Threading; //该命名空间下也包含Timer类
using CountDownTimer = System.Timers.Timer; //别名
public class HelloWorld
{
public static void Main()
{
CountDownTimer timer;
// ...
}
}
}
6,虽然C#允许提前返回,但为了增强代码可读性,应尽量确定单一的退出位置,而不是在方法的各个路径上散布return。
7,方法唯一性取决于方法名、参数类型和参数数量差异。
8,异常的捕获顺序应该由具体到不具体:
public static void Main()
{
try
{
Console.WriteLine("Begin executing");
Console.WriteLine("Throw exception");
throw new Exception("Arbitrary exception");
Console.WriteLine("End executing");
}
catch(FormatException exception)
{
Console.WriteLine(
"A FormatException was thrown");
}
catch(Exception exception)
{
Console.WriteLine(
$"Unexpected error: { exception.Message }");
throw;
}
catch
{
Console.WriteLine("Unexpected error!");
}
Console.WriteLine(
"Shutting down...");
}
需要注意的是,C#程序集中无论从System.Exception派生的类型都会表现的和从从System.Exception派生一样

9,要在捕获并重新抛出异常时使用空的throw语句,以便保留调用栈信息。
C#新知识
1,虽然方法只能有一个返回值,但是可以利用元组,返回多个:
static (string First, string Last) GetName()
{
string firstName, lastName;
firstName = GetUserInput("Enter your first name: ");
lastName = GetUserInput("Enter your last name: ");
return (firstName, lastName);
}
2,表达式主题方法,如果方法只是返回简单语句这种方式,可以使用该方法:
private static string GetFullName(string firstName, string lastName) => $"{firstName}{lastName}";
3,最好将核心功能放到单一方法中供其他重载方法调用。
智能推荐
asp.net本质论基础篇 get请求与post请求
get post请求 1.首先建个文件夹 将建的东西都放在一个文件夹下 (1)建html (2)建个一般处理程序 (3)在建一个一般处理程序 总结内容 学asp.net本质论 get post 后面会经常用,暂时理解一下,不需要刻意记...
Asp.net本质论之HTTP请求处理程序
主题 概要 Asp.net Http请求处理程序 编辑 时间 新建 20171113 序号 参考资料 1 Asp.net本质论 2 https://www.cnblogs.com/RobbinHan/archive/2011/12/05/2270707.html 前面从介绍HttpApplication对像可知,HttpApplication有19个标准事件,在第8个事件PostMapReques...
Asp.net本质论之应用程序对象
主题 概要 Asp.net 应用程序对象 编辑 时间 新建 20171018 序号 参考资料 1 Asp.net本质论 2 https://msdn.microsoft.com/en-us/library/system.web.httpapplication(v=vs.110).aspx 3 https://msdn.microsoft.com/en-us/library/system.web.i...
c#语言------------函数(方法)的高级参数(ref和params参数)
ref参数: ref 能够将一个变量带入一个方法中进行改变,改变完成后再将改变完成后的带出方法 params 可变参数 Test(params int[] arr)将实参列表中跟可变参数数组类型一致的元素都当做数组的元素去处理 params可变参数必须是形参列表中的最后一个元素,其他的参数必须写到params可变参数的前面 params唯一性,一个方法中只能出现一个par...
ASP.net本质论之用控制台应用程序创建Asp.net服务器
主题 概要 Asp.net 应用程序域、HttpRunTime 编辑 时间 新建 20170925 序号 参考资料 1 Asp.net本质论 2 C#高级编程(第七版) 3 http://blog.csdn.net/sh524555685/article/details/7454244(应用程序域解释) 4 http://blog.csdn.net/lhc1105/article/details/...
猜你喜欢
C#参数详解五(参数数组)
参数数组 前面所述的参数类型都必须严格地一个实参对应一个形参。参数数组则不同,它允许零个或多个实参对应一个特殊的形参。 参数数组的重点如下: 在一个参数列表中只能有一个参数数组。 如果有,它必须是列表中的最后一个。 由参数数组表示的所有参数都必须具有相同的类型。 声明一个参数数组必须做的事如下: 在数据类型前使用 params修饰符。 在数据类型后放置一组空的方括号。 下面的方法头展示了int型参...
冒泡排序,及改进方式,性能优化400%>>>附图解加源码
首先源码附上,源码中带有注释,看不懂没关系,源码后面附带图解,最后附上代码效率提升图 源码如下: 方案一:其实实现很简单,两层循环,每次内层迭代出最大的一个值,将其放入数组最后一个位置,外层循环的末端便往前移一位。其原理如下图 方案一代码块: 方案二:优化改进 仔细观察上面的图,我们不难发现当迭代到图下这样的情况时,其实已经全排序好了,但是我们还是需要对它进行1,2,3,4,5,6的迭代,这些情况...
css3常用选择器
先介绍一下基本选择器,如下几种 *:通配符。选择页面的所有元素 E:元素选择器。对页面的一些元素进行选择,如p,div,li等 .class:类选择器 #id:id选择器。一个Id在一个页面中只能选择,这是和类选择器的区别 E F:后代选择器。如ul li。选中ul下的li E>F:子元素选择器。如下,可以用 h1 > strong {color:red;}使h1的子元素strong变...
python is not set from command line or npm configuration
问题 猜测原因 python版本冲突 解决方案 去官网找到最新版本python 点击下载,注意记住你下载的目录,下载好以后,打开这个目录下的文件夹,Python310, 整个文件夹复制一下 在项目里运行npm config list --json 找到python的路径 我的是 C:\\Python27\\python.exe 说明项目里用的还是py...
Java8 Stream流操作
前言 我们常常需要将一个容器转化成另一个容器,或是对这个容器中的数据进行批量处理,这时使用Stream流可以大大减少我们的工作量。 1 Stream概述 Java 8 是一个非常成功的版本,这个版本新增的Stream,配合同版本出现的 Lambda ,给我们操作集合(Collection)提供了极大的便利。 那么什么是Stream? Stream将要处理的元素集合看作一种流,在流的过程中,借助St...
