官方文档地址为C#7.0,下面是我的7.0学习笔记
out 变量
可以直接在参数中声明一个out变量,不需要单独的声明语句
public static void Tout(out string a)
{
a = "a1";
}
out和ref,ref更侧重于修改,而out则是输出,下面是一个out和ref的例子
ref还是需要新声明,而out可以直接在参数中写入,ref是有进有出,而out是只出不进
static void Main(string[] args)
{
string a = "";
Tout(out string test1);
Tref(ref a);
Console.WriteLine(test1);
Console.WriteLine(a);
Console.ReadLine();
}
public static void Tout(out string a)
{
a = "a1";
}
public static void Tref(ref string b)
{
b = "b1";
}
元组
官方的元组详细介绍文档地址元组
先介绍一下python中的元组
python中的几个集合,分别是
list列表,相当于C#中的list集合
list = [ 'a', 786 , 1, 'c', 88 ]
和C#一样支持按下标取值
dictionary字典
dict = {}
dict['one'] = "This is one"
dict[2] = "This is two"
tinydict = {'name': 'john','code':6734, 'dept': 'sales'}
还有就是元组
python中的元组用 () 标识。内部元素用逗号隔开。但是元组不能二次赋值,相当于只读列表。
tuple = ( 'runoob', 786 , 2.23, 'john', 70.2 )
下面再介绍一下C#7.0中的元组
元组是包含多个字段以表示数据成员的轻量级数据结构。
(string a, string b) test = ("a", "b");
Console.WriteLine($"{test.a}+{test.b}");
test.a = "aa";
Console.WriteLine($"{test.a}+{test.b}");
如上代码所示,C#中的元组允许修改。也可以给右侧的字段赋名称,并且声明多少就可以输入多少参数。
(string a, string b,string c) test = ("a", "b","c");
在进行元组赋值时,还可以指定赋值右侧的字段的名称:
(string a, string b) testDicError = (key: "a", value: "b");//不支持这种赋命名 必须要var才能取到key value
var testDic = (key:"a",value:"b");
Console.WriteLine($"{testDic.key}+{testDic.value}");
重点来了,方法返回值可以返回元组类型,如下代码,这个真的是太好用了,返回元素还可以按照名称获取。
public static double StandardDeviation(IEnumerable<double> sequence)
{
(int Count, double Sum, double SumOfSquares) computation = ComputeSumAndSumOfSquares(sequence);
var variance = computation.SumOfSquares - computation.Sum * computation.Sum / computation.Count;
return Math.Sqrt(variance / computation.Count);
}
private static (int Count, double Sum, double SumOfSquares) ComputeSumAndSumOfSquares(IEnumerable<double> sequence)
{
var computation = (count: 0, sum: 0.0, sumOfSquares: 0.0);
foreach (var item in sequence)
{
computation.count++;
computation.sum += item;
computation.sumOfSquares += item * item;
}
return computation;
}
弃元
人如其名,元组中不要的元素,想要丢弃,可以使用一个名为 _(下划线字符)的只写变量,知道就用,不知道就不丢弃该元素,多打一个命名,影响不大,语法使用如下。
using System;
using System.Collections.Generic;
public class Example
{
public static void Main()
{
var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);
Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
}
private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
{
int population1 = 0, population2 = 0;
double area = 0;
if (name == "New York City")
{
area = 468.48;
if (year1 == 1960)
{
population1 = 7781984;
}
if (year2 == 2010)
{
population2 = 8175133;
}
return (name, area, year1, population1, year2, population2);
}
return ("", 0, 0, 0, 0, 0);
}
}
模式匹配
官方介绍如下:模式匹配 是一种可让你对除对象类型以外的属性实现方法分派的功能。模式匹配支持 if 表达式和 switch 表达式。
直接使用官方代码介绍吧,目前只支持if和switch看一下就懂了
if的代码if (input is int count) sum += count;
switch的代码
public static int SumPositiveNumbers(IEnumerable<object> sequence)
{
int sum = 0;
foreach (var i in sequence)
{
switch (i)
{
case 0:
break;
case IEnumerable<int> childSequence:
{
foreach(var item in childSequence)
sum += (item > 0) ? item : 0;
break;
}
case int n when n > 0:
sum += n;
break;
case null:
throw new NullReferenceException("Null found in sequence");
default:
throw new InvalidOperationException("Unrecognized type");
}
}
return sum;
}
Ref 局部变量和返回结果
直接举例子吧
using System;
class NumberStore
{
int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };
public ref int FindNumber(int target)
{
for (int ctr = 0; ctr < numbers.Length; ctr++)
{
if (numbers[ctr] >= target)
return ref numbers[ctr];
}
return ref numbers[0];
}
public override string ToString() => string.Join(" ", numbers);
}
var store = new NumberStore();
Console.WriteLine($"Original sequence: {store.ToString()}");
int number = 16;
ref var value = ref store.FindNumber(number);
value *= 2;
Console.WriteLine($"New sequence: {store.ToString()}");
// The example displays the following output:
// Original sequence: 1 3 7 15 31 63 127 255 511 1023
// New sequence: 1 3 7 15 62 63 127 255 511 1023
本地函数
个人理解可以在方法内声明一个方法使用,
public static IEnumerable<char> AlphabetSubset3(char start, char end)
{
if (start < 'a' || start > 'z')
throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
if (end < 'a' || end > 'z')
throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");
if (end <= start)
throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");
return alphabetSubsetImplementation();
IEnumerable<char> alphabetSubsetImplementation()
{
for (var c = start; c < end; c++)
yield return c;
}
}
可以对 async 方法采用相同的技术,以确保在异步工作开始之前引发由参数验证引起的异常:
public Task<string> PerformLongRunningWork(string address, int index, string name)
{
if (string.IsNullOrWhiteSpace(address))
throw new ArgumentException(message: "An address is required", paramName: nameof(address));
if (index < 0)
throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));
return longRunningWorkImplementation();
async Task<string> longRunningWorkImplementation()
{
var interimResult = await FirstWork(address);
var secondResult = await SecondStep(index, name);
return $"The results are {interimResult} and {secondResult}. Enjoy.";
}
}
throw 表达式
从 C# 7.0 开始,throw 可以用作表达式和语句。 这允许在以前不支持的上下文中引发异常。 这些方法包括:
**条件运算符。**下例使用 throw 表达式在向方法传递空字符串数组时引发 ArgumentException。 在 C# 7.0 之前,此逻辑将需要显示在 if/else 语句中。
private static void DisplayFirstNumber(string[] args)
{
string arg = args.Length >= 1 ? args[0] :
throw new ArgumentException("You must supply an argument");
if (Int64.TryParse(arg, out var number))
Console.WriteLine($"You entered {number:F0}");
else
Console.WriteLine($"{arg} is not a number.");
}
null 合并运算符。 在以下示例中,如果分配给 Name 属性的字符串为 null,则将 throw 表达式与 null 合并运算符结合使用以引发异常。
public string Name
{
get => name;
set => name = value ??
throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null");
}