<li id="2aw4k"></li>
  • <div id="2aw4k"><tr id="2aw4k"></tr></div>
  • <div id="2aw4k"><tr id="2aw4k"></tr></div>
    <center id="2aw4k"><small id="2aw4k"></small></center><center id="2aw4k"><small id="2aw4k"></small></center>
    首页»ASP.NET»C# 7.0 语言新特性

    C# 7.0 语言新特性

    来源:oschina 发布时间:2016-08-25 阅读次数:

      下面是关于在C#7.0语言中计划功能的说明。其?#20889;?#37096;分功能在Visual Studio “15” Preview 4中能运行。现在是最好的试用时期,请记录下你们的想法。

      C#7.0语言增加了许多的新功能,促使专注于数据消费,简化代码和性能。

      或许最大的特征是元组(tuples) ,使得它易于有多个结果,并从而简化代码是以对数据的形?#27425;?#26465;件的模式匹配。但也?#34892;?#22810;其他的一些功能希望将它们结合起来,让代码运行更高效,更明确,从而获得更多的创造性。如果有哪些运行不是你想要的或者有想改进的功能,在Visual Studio的窗口顶部使用“send feedback”功能将结果反馈给我们。在我所描述的许多功能在Preview 4还没有办法充分运行,根据用户的反馈结果,我们将在发布最终版是增加些新的功能。而必须要指出的是,现有计划中的一些功能在最终版也可能会有所改变或取消。

      如果你对这个功能设置?#34892;?#36259;并想学习它,在Roslyn GitHub site上可以?#19994;?#35768;多的设计说明和相关讨论。

      输出(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})");
    }

      需要注意的是,变量是在封闭块?#27573;?#20869;,所以后续可以使用它们。大多数类型的语句不建立自己的适用?#27573;В?#22240;此out变量通常在声明中被引入到封闭?#27573;А?/p>

    :在Preview 4中,适用?#27573;?#35268;则更为严格: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));
    }

      正如你所看到?#27169;?strong>模式变量(变量通过模式引入)与先前描述的 out 变量?#34892;?#31867;似,他们可以在表达式中被声明,而且可以在它们最近的周围?#27573;?#20869;被使用。也像 out 变量那样,模式变量是易变?#27169;?/p>

    : 就像 out 变量一样,严格的?#27573;?#35268;则适用于 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 子句不再是必然不相交?#27169;?#31532;一个子句匹配的将被选择。因此这里重要的是上面代码中 square case 比 rectangle case 来得要早。也是和 catch 子句一样,编译器会通过标记明显不能到达的情况来帮助你。在这之前,你永?#27573;?#27861;知道评价的顺序,所以这不是一个重大改变的特性。
    • 默认子句总是最后被评价:即使上面代码中 null 子句是最后才来,它会在默认子句被选择前被检查。这是为了与现有 switch 语义相兼容。然而,好的做法通常会让你把默认子句放到最后。
    • null 子句在最后不可到达:这是因为类型模式遵循当前的 is 表达式的例子并?#20063;?#20250;匹配空值。这保证了空值不会偶然被任何的类型模式捎来第一抢购。你必须更明确如何处理它们(或为默认子句留下他们)。

      通过 case ...: 标签引入的模式变量仅存在于相对应的 switch 部分的?#27573;?#20869;。

      元组

      这是常见的希望从一个方法返回多个值的做法。目前可用的选项不是最佳?#27169;?/p>

    • 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
    }

      这个方法目前?#34892;?#22320;返回三个字符串,将其作为元素在元组类型里包裹起来。

      方法的调用者将会接受到一个元组,并且可以逐一访?#35797;?#32032;。

    var names = LookupName(id);
    WriteLine($"found {names.Item1} {names.Item3}.");

      Item1 等等,是元组元素的默认名字,并能够经常被使用。但它们不是太好描述?#27169;?#22240;此你可以选择性地添加更好的一个。

    (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

      通常来说,你可以互相分配元组类型无关的名字,只要独立的元素是可以被分配?#27169;?#20803;组类型会自如 转换成其他元组类型。特别?#23884;?#20110; tuple literals ,存在一些限制,这会警告或提示在常见的错误的情况下提示,例如偶然交换元素的名字。

    注意:这些限制还没在 Preview 4 中实现

      元组是值类型,而且他们的元素只是公开、易变的域。他们的值相等,代表这两个元组是相等?#27169;ǘ加?#30456;同的哈斯码)如果它们的元素都结?#20113;?#37197;(?#21152;?#30456;同的哈斯码)。

      这使得元组对于在多种返回值下的很多情况十分有用。举例来说,如果你需要一个有多种键的?#23454;洌?#20351;用元组作为你的键,然后一切东西就会如常工作。如果你需要在每个位置有一个有多种值的列表,使用元组,查找列表等等,程序会正常运行。

    注意:元组?#35272;?#19968;系列底层类型,它们在 Preview 4 中不被引入。为了将来的工作,你可以通过 NuGget 轻易获取它们: 在 Solution Explorer 中右键点击项目,并选择“Manage NuGet Packages…” 选择“Browse”选项卡,检查“Include prerelease” 并?#24050;?#25321;“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

      不仅仅元组可以被解构,任何类型都可以被解构,只要有一个对应?#27169;?#23454;体或者扩展)解构方法:

    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);

      这将成为一种常见模式,包含析构函数和“对称”解析:

      针对输出变量,我们计划在解构?#24615;?#35768;使用“通配符”:

    (var myX, *) = GetPoint(); // I only care about myX
       注:仍然还没有确定是否将通配符引入C# 7.0中。

      局部函数

      有时,一个辅助函数只在一个使用它的单一方法内部有意义。现在你可以在其他功能体内?#21487;?#26126;这些函数作为一个局部函数:

    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表达式。

      举一个例子,方法实现迭代器通常需要严格检查调用时非迭代器封装方法。(迭代器本身没?#24615;?#34892;,只到调用MoveNext 才会运行)。局部函数在这种情况下是完?#36182;模?/p>

    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 允许使用“_”作为数字?#25351;?#31526;在数字literals中:

    var d = 123_456;
    var x = 0xAB_CD_EF;

      你可以把它放在你想要的位置,提升可读性。这样对数?#24471;?#26377;任何影响。

      此外,C# 7.0也介绍了二进制literals,这样你可以直接指定二进制模式而不必知道十六进制符号。

    var b = 0b1010_1011_1100_1101_1110_1111;

      Ref 返回和本地

      就像你可以通过reference(用ref修饰符)在C#?#20889;?#36882;东西,您现在可以通过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 到这样一个结构,且通过调用者可以读取和修改它。

      这里有一些限制,以确保这?#21069;?#20840;?#27169;?/p>

    • 你可以只返回 refs 那些是 “安全返回(safe to return)”的:那些被传递给你?#27169;?#21644;那些点到对象的字段。

    • Ref locals被初始化为某一存储位置,并?#20063;?#33021;突变到指向另一个。

      广义异步返回类型

      截至目前为止,在C#调用异步方法必须要返回void,Task或Task<T>。C#7.0允许以这样的方式来定义其它类型,从而使它们可以从异步方法返回。

      例如,我们计划有一个ValueTask<T>结构类型。它是建立在预防Task<T> 对象的分配时,异步操作的结果是?#35328;?#21487;等候的时间的情况下。对于很多异步场景,比如以涉及缓冲为例, 这可以大大减少分配的数量,并使性能有显著提升。

      这里?#34892;?#22810;其他的方法可以让您想象自定义“task-like”类型是有用的。它不会是简单的正确创建,所以我们不要指望大多数人推出自己?#27169;?#20294;他们很可能将会开始在框架和API展现出来,然后调用方可以返回并await他们今天做任务(Tasks)的方式。

        注:广义异步返回类型?#24418;从?#29992;在预览4。    

      更多 Expression-bodied 方法

      Expression-bodied 方法、属性等都是C# 6.0的重大突破,但并不允许他们在各?#25351;?#26679;的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#团队。并且,开源!

      在表达式的中间抛出一个异常是很容易?#27169;?#21482;需要为你自己调用一个方法,但在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();
    }
      注:抛出表达?#20132;?#26410;在预览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/

    QQ群:WEB开发者官方群(515171538),验证消息:10000
    微信群:加小编微信 849023636 邀请您加入,验证消息:10000
    提示:更多精彩内容关注微信公众号:全栈开发者中?#27169;╢sder-com)
    C#
    网友评论(共0条评论) 正在载入评论......
    理智评论文明上网,拒绝恶意谩骂 发表评论 / 共0条评论
    登录会员中心
    大乐透彩票预测
    <li id="2aw4k"></li>
  • <div id="2aw4k"><tr id="2aw4k"></tr></div>
  • <div id="2aw4k"><tr id="2aw4k"></tr></div>
    <center id="2aw4k"><small id="2aw4k"></small></center><center id="2aw4k"><small id="2aw4k"></small></center>
    <li id="2aw4k"></li>
  • <div id="2aw4k"><tr id="2aw4k"></tr></div>
  • <div id="2aw4k"><tr id="2aw4k"></tr></div>
    <center id="2aw4k"><small id="2aw4k"></small></center><center id="2aw4k"><small id="2aw4k"></small></center>