C#7.0言语增添了许多的新功用,促使专注于数据消耗,简化代码和机能。
也许最大的特征是元组(tuples) ,使得它易于有多个效果,并从而简化代码是以对数据的外形为前提的情势婚配。但也有许多其他的一些功用愿望将它们结合起来,让代码运转更高效,更明白,从而取得更多的创造性。假如有哪些运转不是你想要的或许有想革新的功用,在Visual Studio的窗口顶部运用“send feedback”功用将效果反应给我们。在我所形貌的许多功用在Preview 4还没有要领充足运转,依据用户的反应效果,我们将在宣布最终版是增添些新的功用。而必须要指出的是,现有设计中的一些功用在最终版也能够会有所转变或作废。
假如你对这个功用设置感兴趣并想进修它,在Roslyn GitHub site上能够找到许多的设计申明和相干议论。
输出(out)变量
如今在C#中,运用out参数并不像我们设想中那末流通。在运用out参数挪用要领时,你起首必需声明变量通报给它。虽然你一般不会初始化这些变量(他们将经由过程该要领后统统被掩盖),也不能运用VAR来声明他们,然则须要指定完全的范例:
public void PrintCoordinates(Point p) { int x, y; // have to "predeclare" p.GetCoordinates(out x, out y); WriteLine($"({x}, {y})"); }
在C#7.0,我们增添了Out变量,作为out参数通报的点来声明一个变量权:
public void PrintCoordinates(Point p) { p.GetCoordinates(out int x, out int y); WriteLine($"({x}, {y})"); }
须要注重的是,变量是在关闭块局限内,所以后续能够运用它们。大多数范例的语句不竖立自身的适用局限,因而out变量一般在声明中被引入到关闭局限。
注:在Preview 4中,适用局限划定规矩更加严厉:out变量的作用域为它们在声明的说法。因而,上面的例子不会在后续的版本中运用。
由于out变量直接声明作为参数通报给out参数,编译器一般能够示知范例(除非有争执的过载)。所以这是很好用VAR,而不是一个范例来声明它们:
p.GetCoordinates(out var x, out var y);
out参数的一个罕见的用处是Try...情势,个中out参数一个boolean return示意胜利,out参数举行获得的效果:
public void PrintStars(string s) { if (int.TryParse(s, out var i)) { WriteLine(new string('*', i)); } else { WriteLine("Cloudy - no stars tonight!"); } }
注:Preview 4处置惩罚的比较好的处所在于只是用if语句定义它。
设计许可“wildcards”作为out参数以及在*的情势,无视不主要的out参数:
p.GetCoordinates(out int x, out *); // I only care about x
注:wildcards可否把它变成C#7.0照样个未知数。
情势婚配
C# 7.0 引入了情势的观点,抽象地说,这是一种语法身分能够用来测试一个值是不是有一个肯定的“形”以及在它起作用时从值内里猎取到的分外信息。
下面是 C# 7.0 中关于情势的例子:
c 的常量情势(c 是C#中的一个常量表达式),用于测试输入的参数是不是和 c 相称
T x 的范例情势(T 是一个范例,x 是一个标识符),用于测试输入的参数是不是有范例 T,假如有,提取输入参数的值到一个 T 范例的新 x 变量中。
var x 变量情势(x 是一个标识符),一般会婚配并简朴地将输入参数的值放进一个新变量 x 中
这是个最先,情势是一种新的 C# 言语元素,而且我们未来能够把它们更多地增添到 C# 中。
在 C# 7.0 中,我们正在运用情势以加强两种已存在的言语组织:
is 表达式如今在右侧能够有一个情势,而不只是一个范例
case 子句在 switch 语句中如今能够经由过程情势婚配,而不单单议是常量值
在未来的C#中,我们也许会增添更多能运用情势的处所。
带情势的 Is 表达式
这是一个运用带有常量情势和范例情势的 is 表达式的例子:
public void PrintStars(object o) { if (o is null) return; // constant pattern "null" if (!(o is int i)) return; // type pattern "int i" WriteLine(new string('*', i)); }
正如你所看到的,情势变量(变量经由过程情势引入)与先前形貌的 out 变量有些相似,他们能够在表达式中被声明,而且能够在它们近来的四周局限内被运用。也像 out 变量那样,情势变量是易变的,
注: 就像 out 变量一样,严厉的局限划定规矩适用于 Preview 4.
情势和 Try 要领一般会一同涌现:
if (o is int i || (o is string s && int.TryParse(s, out i)) { /* use i */ }
带情势的 Switch 语句
我们正在泛化 switch 语句,因而:
你能够在任何范例上运用 switch(不单单议是原始范例)
能够在 case 子句中运用情势
Case 子句能够具有分外的前提
这里是一个简朴的例子:
switch(shape) { case Circle c: WriteLine($"circle with radius {c.Radius}"); break; case Rectangle s when (s.Length == s.Height): WriteLine($"{s.Length} x {s.Height} square"); break; case Rectangle r: WriteLine($"{r.Length} x {r.Height} rectangle"); break; default: WriteLine("<unknown shape>"); break; case null: throw new ArgumentNullException(nameof(shape)); }
有几件关于这个新扩大的 switch 语句的事须要注重:
case 子句的递次如今很主要:就像 catch 子句,case 子句不再是必定不订交的,第一个子句婚配的将被挑选。因而这里主要的是上面代码中 square case 比 rectangle case 来得要早。也是和 catch 子句一样,编译器会经由过程标记明显不能抵达的状况来协助你。在这之前,你永久没法晓得评价的递次,所以这不是一个严重转变的特征。
默许子句老是末了被评价:纵然上面代码中 null 子句是末了才来,它会在默许子句被挑选前被搜检。这是为了与现有 switch 语义相兼容。但是,好的做法一般会让你把默许子句放到末了。
null 子句在末了不可抵达:这是由于范例情势遵照当前的 is 表达式的例子而且不会婚配空值。这保证了空值不会偶然被任何的范例情势捎来第一抢购。你必需更明白怎样处置惩罚它们(或为默许子句留下他们)。
经由过程 case ...: 标签引入的情势变量仅存在于相对应的 switch 部份的局限内。
元组
这是罕见的愿望从一个要领返回多个值的做法。如今可用的选项不是最好的:
Out 参数。运用愚笨(即使有上面形貌到的提拔),它们不运用异步的要领运转。
System.Tuple<...> 返回范例。运用累坠而且须要一个元组对象的分派。
为每一个要领定制传输范例:大批的代码为了范例开支的目标仅是暂时网络一些值
匿名范例经由过程返回一个 dynamic 返回范例。高机能开支而且没有静态范例搜检。
为了在这方面做得更好,C# 增加了tuple types 和 tuple literals:
(string, string, string) LookupName(long id) // tuple return type { ... // retrieve first, middle and last from data storage return (first, middle, last); // tuple literal }
这个要领如今有效地返回三个字符串,将其作为元素在元组范例里包裹起来。
要领的挪用者将会接受到一个元组,而且能够一一接见元素。
var names = LookupName(id); WriteLine($"found {names.Item1} {names.Item3}.");
Item1 等等,是元组元素的默许名字,并能够经常被运用。但它们不是太好形貌的,因而你能够挑选性地增加更好的一个。
(string first, string middle, string last) LookupName(long id) // tuple elements have names
如今元组的接受者具有更多的可形貌的名字用于运转:
var names = LookupName(id); WriteLine($"found {names.first} {names.last}.");
你也能够在 tuple literals 中直接指定名字:
return (first: first, middle: middle, last: last); // named tuple elements in a literal
一般来讲,你能够相互分派元组范例无关的名字,只需自力的元素是能够被分派的,元组范例会自若 转换成其他元组范例。特别是关于 tuple literals ,存在一些限定,这会正告或提醒在罕见的毛病的状况下提醒,比方偶然交流元素的名字。
注重:这些限定还没在 Preview 4 中完成
元组是值范例,而且他们的元素只是公然、易变的域。他们的值相称,代表这两个元组是相称的(都有雷同的哈斯码)假如它们的元素都结对婚配(都有雷同的哈斯码)。
这使得元组关于在多种返回值下的许多状况非常有效。举例来讲,假如你须要一个有多种键的辞书,运用元组作为你的键,然后统统东西就会如常事情。假如你须要在每一个位置有一个有多种值的列表,运用元组,查找列表等等,顺序会一般运转。
注重:元组依靠一系列底层范例,它们在 Preview 4 中不被引入。为了未来的事情,你能够经由过程 NuGget 随意马虎猎取它们: 在 Solution Explorer 中右键点击项目,并挑选“Manage NuGet Packages…” 挑选“Browse”选项卡,搜检“Include prerelease” 而且挑选“nuget.org”作为“Package source” 搜刮“System.ValueTuple”并装置它
解构
另一种消弭元组(tuple)的要领是解构元组。经由过程一个解构声明语法,把一个元组(或许其他的值)拆分为几部份,而且从新定义为新的变量。
(string first, string middle, string last) = LookupName(id1); // deconstructing decla rationWriteLine($"found {first} {last}.");
在解构中可采纳var关键字:
(var first, var middle, var last) = LookupName(id1); // var inside
或许把var关键字提取出来,在括号外:
var (first, middle, last) = LookupName(id1); // var outside
你也能够经由过程解构赋值来解构一个现有变量:
(first, middle, last) = LookupName(id2); // deconstructing assignment
不单单议元组能够被解构,任何范例都能够被解构,只需有一个对应的(实体或许扩大)解构要领:
public void Deconstruct(out T1 x1, ..., out Tn xn) { ... }
输出参数由解构以后的效果值组成。
(为何采纳数据参数替代返回一个元组?如许,你能够重载多个差别的数值)
class Point { public int X { get; } public int Y { get; } public Point(int x, int y) { X = x; Y = y; } public void Deconstruct(out int x, out int y) { x = X; y = Y; } } (var myX, var myY) = GetPoint(); // calls Deconstruct(out myX, out myY);
这将成为一种罕见情势,包括析构函数和“对称”剖析:
针对输出变量,我们设计在解构中许可运用“通配符”:
(var myX, *) = GetPoint(); // I only care about myX
注:依然还没有肯定是不是将通配符引入C# 7.0中。
部分函数
偶然,一个辅佐函数只在一个运用它的单一要领内部有意义。如今你能够在其他功用体内部声明这些函数作为一个部分函数:
public int Fibonacci(int x) { if (x < 0) throw new ArgumentException("Less negativity please!", nameof(x)); return Fib(x).current; (int current, int previous) Fib(int i) { if (i == 0) return (1, 0); var (p, pp) = Fib(i - 1); return (p + pp, p); } }
参数和闭合区间部分变量可用在部分函数内,相似lambda表达式。
举一个例子,要领完成迭代器一般须要严厉搜检挪用时非迭代器封装要领。(迭代器自身没有运转,只到挪用MoveNext 才会运转)。部分函数在这类状况下是圆满的:
public IEnumerable<T> Filter<T>(IEnumerable<T> source, Func<T, bool> filter) { if (source == null) throw new ArgumentNullException(nameof(source)); if (filter == null) throw new ArgumentNullException(nameof(filter)); return Iterator(); IEnumerable<T> Iterator() { foreach (var element in source) { if (filter(element)) { yield return element; } } } }
假如迭代器是一个私有要领的下一个过滤器,它将有能够被其他成员不小心运用(没有参数搜检)。另外,作为过滤器,它将须要采用统统的雷同的参数,而不是指定域内的参数。
注:在Preview 4版本中,当地函数必需在它们被挪用之前声明。这个限定将被放松,能挪用读取直接赋值的部分变量。
Literal 革新
C# 7.0 许可运用“_”作为数字分隔符在数字literals中:
var d = 123_456; var x = 0xAB_CD_EF;
你能够把它放在你想要的位置,提拔可读性。如许对数值没有任何影响。
另外,C# 7.0也引见了二进制literals,如许你能够直接指定二进制情势而没必要晓得十六进制标记。
var b = 0b1010_1011_1100_1101_1110_1111;
Ref 返回和当地
就像你能够经由过程reference(用ref修饰符)在C#中通报东西,您如今能够经由过程reference return 他们,并经由过程 reference将它们存储在部分变量中。
public ref int Find(int number, int[] numbers) { for (int i = 0; i < numbers.Length; i++) { if (numbers[i] == number) { return ref numbers[i]; // return the storage location, not the value } } throw new IndexOutOfRangeException($"{nameof(number)} not found"); } int[] array = { 1, 15, -39, 0, 7, 14, -12 }; ref int place = ref Find(7, array); // aliases 7's place in the array place = 9; // replaces 7 with 9 in the array WriteLine(array[4]); // prints 9
这对绕过占位符成为大数据组织黑白常有效的。举例来讲,一个游戏能够会在一个大的预分派数组组织中保留其数据(为防止垃圾网络停息)。Methods 能够直接返回一个 reference 到如许一个组织,且经由过程挪用者能够读取和修正它。
这里有一些限定,以确保这是平安的:
你能够只返回 refs 那些是 “平安返回(safe to return)”的:那些被通报给你的,和那些点到对象的字段。
Ref locals被初始化为某一存储位置,而且不能突变到指向另一个。
广义异步返回范例
停止如今为止,在C#挪用异步要领必须要返回void,Task或Task<T>。C#7.0许可以如许的体式格局来定义别的范例,从而使它们能够从异步要领返回。
比方,我们设计有一个ValueTask<T>组织范例。它是竖立在防备Task<T> 对象的分派时,异步操纵的效果是已在可期待的时候的状况下。关于许多异步场景,比如以触及缓冲为例, 这能够大大削减分派的数目,并使机能有明显提拔。
这里有许多其他的要领能够让您设想自定义“task-like”范例是有效的。它不会是简朴的准确建立,所以我们不要希望大多数人推出自身的,但他们极能够将会最先在框架和API展示出来,然后挪用方能够返回并await他们本日做使命(Tasks)的体式格局。
注:广义异步返回范例还没有应用在预览4。
更多 Expression-bodied 要领
Expression-bodied 要领、属性等都是C# 6.0的严重突破,但并不许可他们在林林总总的member中运用,C#7.0增加了接见器、组织函数和闭幕器等,使更多member能够运用Expression-bodied 要领:
class Person { private static ConcurrentDictionary<int, string> names = new ConcurrentDictionary<int, string>(); private int id = GetId(); public Person(string name) => names.TryAdd(id, name); // constructors ~Person() => names.TryRemove(id, out *); // destructors public string Name { get => names[id]; // getters set => names[id] = value; // setters } }
注:这些分外的Expression-bodied 要领还没有事情在预览4。
这是一个由社区孝敬的特征,而非微软C#团队。而且,开源!
在表达式的中心抛出一个异常是很轻易的,只须要为你自身挪用一个要领,但在C#7.0中我们许可在一些处所直接抛出表达式:
class Person { public string Name { get; } public Person(string name) => Name = name ?? throw new ArgumentNullException(name); public string GetFirstName() { var parts = Name.Split(" "); return (parts.Length > 0) ? parts[0] : throw new InvalidOperationException("No name!"); } public string GetLastName() => throw new NotImplementedException(); }
注:抛出表达式还未在预览4事情。
本文地点:http://www.oschina.net/translate/whats-new-in-csharp-7-0
原文地点:https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/
以上就是C# 7.0 言语新特征的细致内容,更多请关注ki4网别的相干文章!