C# 版本 7.0 新增特性
发布时间:2017 年 3 月
C# 7.0 版已与 Visual Studio 2017 一起发布。 此版本继承和发展了 C# 6.0。 以下介绍了部分新增功能:
其他功能包括:
所有这些功能都为开发者提供了新功能,帮助编写比以往任何时候都简洁的代码。 重点是缩减了使用 out
关键字的变量声明,并通过元组实现了多个返回值。 .NET Core 现在面向所有操作系统,着眼于云和可移植性。 语言设计者除了推出新功能外,也会在这些新功能方面付出时间和精力。
参考文章:
详解C#7.0新特性 - cnxy - 博客园
C#7.0 新增功能 - 张传宁 - 博客园
笔记
out 变量
if (int.TryParse(input, out int result)) // 使用内联式声明out变量,而不必提前声明
Console.WriteLine(result);
else
Console.WriteLine("Could not parse input");
// 也可以使用推断类型
if (int.TryParse(input, out var answer))
...
元组和析构函数
(string Alpha, string Beta) namedLetters = ("a", "b"); // 为元素中成员提供语义名称
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");
var alphabetStart = (Alpha: "a", Beta: "b"); // 也可以在右侧指定字段名称
Console.WriteLine($"{alphabetStart.Alpha}, {alphabetStart.Beta}");
// 解构元组
(int max, int min) = Range(numbers); // 当方法返回值是无组类弄时,对返回值进行解包
Console.WriteLine(max);
Console.WriteLine(min);
// 为类添加析构函数(也叫解构器),关键字 Deconstruct
public class Point
{
public double X { get; }
public double Y { get; }
public Point(double x, double y) => (X, Y) = (x, y);
public void Deconstruct(out double x, out double y) => (x, y) = (X, Y);
}
var p = new Point(3.14, 2.71);
(double X, double Y) = p;
模式匹配 is/switch/when
if (input is int count) // 使用is进行模式匹配
sum += count;
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: // 附加了when 条件的类型模式
sum += n;
break;
case null: // null 模式
throw new NullReferenceException("Null found in sequence");
default: // 默认事例
throw new InvalidOperationException("Unrecognized type");
}
本地函数 (局部函数)
本地函数是一种嵌套在另一成员中的类型的方法。 仅能从其包含成员中调用它们。 可以在以下位置中声明和调用本地函数:
- 方法(尤其是迭代器方法和异步方法)
- 构造函数
- 属性访问器
- 事件访问器
- 匿名方法
- Lambda 表达式
- 终结器
- 其他本地函数
但是,不能在 expression-bodied 成员中声明本地函数。
private static string GetText(string path, string filename)
{
var reader = File.OpenText($"{AppendPathSeparator(path)}{filename}");
var text = reader.ReadToEnd();
return text;
// 👇 方法中的方法,也支持使用lambda写类似的函数
string AppendPathSeparator(string filepath)
{
return filepath.EndsWith(@"\") ? filepath : filepath + @"\";
}
}
已扩展 expression bodied 成员
允许表达式的成员进行了扩展:属性、构造函数、析构函数、get
和 set
访问器、索引器
public class ExpressionMembersExample
{
// Expression-bodied 构造函
public ExpressionMembersExample(string label) => this.Label = label;
// Expression-bodied 终结器
~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");
private string label;
// Expression-bodied get / set
public string Label
{
get => label;
set => this.label = value ?? "Default label";
}
// Expression-bodied indexers
public string this[string name] => Convert.ToBase64String(Encoding.UTF8.GetBytes(name));
}
ref 局部变量
声明局部变量并在变量类型之前添加 ref
关键字时,即声明了 reference 变量,或称 ref 局部变量。
ref int aliasOfvariable = ref variable;
// 可以理解为指向引用的引用,但它们引用的数据都是同一个堆上的数据
reference 变量是引用另一个变量(称为引用)的变量。也就是说,reference 变量是其引用的另名。向 reference 变量赋值时,该值将分配给引用。读取 reference 变量的值时,将返回引用的值。
引用返回
默认情况下, return 语句返回表达式的值。以返回对变量的引用。 引用返回值(或 ref 返回值)是由方法按引用向调用方返回的值。 即是说,调用方可以修改方法所返回的值,此更改反映在所调用方法中的对象的状态中。 为此,请使用带 ref
关键字的 return
语句,如以下示例所示:
int[] xs = new int [] {10, 20, 30, 40 };
ref int found = ref FindFirst(xs, s => s == 30);
found = 0;
Console.WriteLine(string.Join(" ", xs)); // output: 10 20 0 40
ref int FindFirst(int[] numbers, Func<int, bool> predicate)
{
for (int i = 0; i < numbers.Length; i++)
{
if (predicate(numbers[i]))
{
return ref numbers[i];
}
}
throw new InvalidOperationException("No element satisfies the given condition.");
}
❓ 难道平时使用返回的值是一个副本?与返回的原值不是堆上的同一个数据吗?
弃元
(_, _, area) = city.GetCityInformation(cityName);
var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);
var (fName, _, city, _) = p; // 解构
static void ProvidesFormatInfo(object? obj) => Console.WriteLine(obj switch { IFormatProvider fmt => $"{fmt.GetType()} object",
null => "A null object reference: Its use could result in a NullReferenceException",
_ => "Some object type without format information"
});
// 👆 这种写法太超前,不懂❓
二进制文本和数字分隔符
整数文本可以是
- 十进制:不使用任何前缀
- 十六进制:使用
0x
或0X
前缀 - 二进制:使用
0b
或0B
前缀
下面的代码演示每种类型的示例:
var decimalLiteral = 42;
var hexLiteral = 0x2A;
var binaryLiteral = 0b_0010_1010;
前面的示例还演示了如何将 _
用作数字分隔符。 可以将数字分隔符用于所有类型的数字文本。
二次解读:
var one = 0b0001;
var sixteen = 0b0001_0000;
long salary = 1000_000_000;
decimal pi = 3.141_592_653_589m;
注:二进制文本是以0b(零b)开头,字母不区分大小写;数字分隔符只有三个地方不能写:开头,结尾,小数点前后。
总结:二进制文本,数字分隔符 可使常量值更具可读性。
引发表达式
throw
表达式
还可以将 throw
用作表达式。 这在很多情况下可能很方便,包括:
- 条件运算符。 以下示例使用
throw
表达式在传递的数组args
为空时引发 ArgumentException:
string first = args.Length >= 1
? args[0]
: throw new ArgumentException("Please supply at least one argument.");
- null 合并运算符。 以下示例使用
throw
表达式在要分配给属性的字符串为null
时引发 ArgumentNullException:
public string Name
{
get => name;
set => name = value ??
throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null");
}
- expression-bodied lambda 或方法。 以下示例使用
throw
表达式引发 InvalidCastException,以指示不支持转换为 DateTime 值:
DateTime ToDateTime(IFormatProvider provider) =>
throw new InvalidCastException("Conversion to a DateTime is not supported.");
其它 ValueTask<int>
从异步方法返回 Task
对象可能在某些路径中导致性能瓶颈。 Task
是引用类型,因此使用它意味着分配对象。 如果使用 async
修饰符声明的方法返回缓存结果或以同步方式完成,那么额外的分配在代码的性能关键部分可能要耗费相当长的时间。 如果这些分配发生在紧凑循环中,则成本会变高。
新语言功能意味着异步方法返回类型不限于 Task
、Task<T>
和 void
。 返回类型必须仍满足异步模式,这意味着 GetAwaiter
方法必须是可访问的。 作为一个具体示例,已将 ValueTask
类型添加到 .NET framework 中,以使用这一新语言功能:
public async ValueTask<int> Func()
{
await Task.Delay(100);
return 5;
}
本文作者:石起起
本文链接:https://www.cnblogs.com/myshiqiqi/p/18699884
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步