在昨天WR发布了vs17,vs17可以使用C#7.0,在之前,我写有一篇博客,关于C#7.0,参见:http://lindexi.oschina.io/lindexi/post/C-7.0/

但是WR砍了一些功能,添加了一些功能。

于是我重新写一篇,关于C# 7.0

C# 7.0 可以在 VS 17 使用,这个版本我下载企业版上传百度云,有需要可以到文章最后去[下载](#VS 17 下载)。

本文主要:C# 7.0 带来的新功能

  • out 返回值定义

  • Tuples

  • 模式匹配

  • ref 返回本地值

  • 内部函数

  • 全部地方可以支持辣么大

  • throw 表达式

  • 广义异步返回类型

  • 数值常量语法

C# 7.0的功能主要是数据处理,让代码更简洁,让代码性能更高

让代码简单这个我觉得不如6.0,性能WR为了Iot做的。C#速度差,垃圾wr就让C#可以直接访问内存,让速度变快,这个下面没有说

C# 7.0 最好的是 使用 Tuple 。虽然之前也有,但是现在版本比较好用。实际抄袭了某脚本。

修改大的有 Case 。模式匹配,可以判断类型,其实这个使用是我们有类 a,类b、c继承a,这时使用就比较好,如何使用在下面会说。

如果觉得这个功能没有用,可以去 Visual studio 按反馈喷

如果好奇他是怎么弄,可以查看https://github.com/dotnet/roslyn

out 返回值定义

我们以前要使用out 总是需要在外面定义我们变量。

首先定义一个 变量,使用函数,这样觉得需要多写代码

  public void PrintCoordinates(Point p)
  {
    int x, y; // 在外面定义
    p.GetCoordinates(out x, out y);
    WriteLine($"({x}, {y})");
  }

在7.0我们可以使用在out定义我们变量,这样看起来不是在一个区域,但是可以减少我的代码

public void PrintCoordinates(Point p)
{
    p.GetCoordinates(out int x, out int y);
    WriteLine($"({x}, {y})");
}

在out定义类型,定义可以用var

看到这我才说这样有用,如果我们开始没有确定我们返回的是什么,然后直接定义,需要修改地方多,但是如果我们使用Var就可以让我们定义修改少,一般在写就需要先想我们需要用什么,不要总是改

如果我们使用一个返回为bool,那么可以在{使用out的值


public void PrintStars(string s)
{
    //转换,可以是数字,显示
    if (int.TryParse(s, out var i)) { WriteLine(new string('*', i)); }
    else { WriteLine("Cloudy - no stars tonight!"); }
}

== 下面代码被WR删了,以前有说到,现实wr没有做

如果有返回值我们不需要,可以out *,这样我们就不用知道这个返回值,原先不需要使用我还要想一个变量,然后vs说我这个没有使用,现在我们直接就不给他名

在我们下面有返回多个,这时不需要的可以用*


public void PrintStars(string s)
{
    //转换,可以是数字,显示
    if (int.TryParse(s, out *)) { WriteLine("转换成功"); }
    else { WriteLine("转换失败"); }
}

== 上面代码WR没有做,不需要的返回值是可以使用_

模式匹配

模式匹配是包括 is 和 switch ,下面先说 is

C# 7.0可以使用 is 一部分代替 as

我们需要判断一个值是否是一个类型,如果是那么赋值,在以前,使用的代码需要两行

    if(o is int)
    {

        int i=(int) o;
    }

还可以使用 as

    int? i = o as int;

但是在新的C#,可以使用

o is int i

那么我们就可以直接使用i

在我们一个判断,如果我们存在了object o是int,那么我们就使用int i=(int)o;

如果我们object不是int,那么转换object o是string,string s=(string)o;

这也就是对下面的语句

int.TryParse(s,out i);

我们可以简化,判断是不是int,如果是给i,这时就返回true

然后判断是不是string,是就转,成功使用i

if (o is int i || (o is string s && int.TryParse(s, out i)) { /* use i */ }

但是 is 的用法在于 switch

我们在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));
}

case 顺序很重要,可以看到可以判断类型,但是 case 还可以混合判断。

                switch (item)
                {
                    default:
                        throw new InvalidOperationException("unknown item type"); 

                    case 0:
                        break;

                    case int val:
                        sum += val;
                        break;

                    case var @var when (@var != null && (int) (@var) == 45):
                        break;

                    //  The order of case clauses now matters!
                    case IEnumerable<object> subList when subList.Any():
                        sum += Sum(subList);
                        break;

                    case IEnumerable<object> subList:
                        break;

                    case null:
                        break;
                }

注意 default 在最后,即使他后面有语句,除非存在语句识别,那么最后会执行他。

Tuples

以前我们需要返回多个有点难,可以使用out参数,可以Tuples<string,double>

我们做了修改,可以使用新的方法,这样我们返回多个就可以直接和某垃圾语言那返回


(string, string, string) LookupName(long id) // tuple return type
{
    ... // 返回多个数据,我们在数据拿到多个数据
    return (first, middle, last); // tuple literal
}

var names = LookupName(id);

我们这样用第一返回值:names.Item1和原来几乎没有修改,这样对于返回值不好,因为我们难以去记,哪个返回值是什么

我们要给他一个好记的 变量,可以写在函数定义

(string first, string middle, string last) LookupName(long id)

我们使用第一个names.first,这样使用就容易,原因是可以给一个表达他是意思的变量。

返回可以使用return (first, middle, last);,必须和之前定义顺序一样,但如果定义了名称,可以使用

return last:last,first:first

这个方法是很好的,不需要和定义的顺序那样。

对于调用函数,可以使用一个变量,可以使用多个变量

    (string first, string middle, string last) = LookupName(id1);
    var name = LookupName(id1);

可以看到两个代码,作用一样,但是第一个代码除了使用变量类型,同样可以使用 var

    (var fist,var midd)=Lookup(id);

如果我们有多个var,那么我们可以简单var (first, middle, last) = LookupName(id1);定义所有变量

我们不关系第二个返回值,可以使用(var first,*)=Lookup(id);

除了方法使用,可以在变量使用

                var sumNew = (first: 1, count: 20);

这样就定义了一个,可以使用他的名称,不使用 item原先的,也就是在定义,给他变量。

上面代码的意思:可以定义一个包括每项名称的变量,可以在使用时,用定义的变量

            var sumNew = (first: 1, count: 20);
            Console.WriteLine($"first {sumNew.first}  count {sumNew.count}");

如果不想在定义写变量,那么可以修改var,作为变量

      (int first, int count) sum = ( 1,  20);
      Console.WriteLine($"first {sum.first}  count {sum.count}");

这里,类型int不能写 var

如果想不到变量,那么只能使用

            (int , int ) sum = ( 1,  20);
            Console.WriteLine($"first {sum.Item1}  count {sum.Item2}");

本地函数

我们可以在函数里面定义函数,这是本地函数

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

以前有些函数只会使用一次,但是他的功能多,所以就把它写成方法,于是一个类就很多这种方法,只会在一个函数使用,但是写成方法,有时候开始看他,会觉得方法很多,不知道哪个方法在哪使用。

上面说的是那些没使用 vs 企业版的兄弟,其实有了企业版,没有这问题。

现在可以使用内部函数,在一个函数里定义函数,看上面的代码,写一个斐波纳算法,可以直接使用函数里函数,不需要定义方法。

这个用法在:迭代,异步

对于迭代器,抛出异常在使用,不是创建。

看下这代码

public static IEnumerable<char> AlphabetSubset(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)}");
    for (var c = start; c < end; c++)
        yield return c;
}

在输入不合法,就会抛出异常,那么抛出异常的时候是什么

    var resultSet = Iterator.AlphabetSubset('f', 'a');
Console.WriteLine("iterator created");
foreach (var thing in resultSet)
  {
    Console.Write($"{thing}, ");
  }

可以看到在 var resultSet = Iterator.AlphabetSubset('f', 'a');不会抛出异常,在 Console.Write($"{thing}, ");抛出异常。

很难定位到是在哪的异常,出现异常和知道异常的,不在一个地方,这就是之前使用迭代的一个比较难发现的。

所以做法是新建一个方法迭代

    public static IEnumerable<char> AlphabetSubset2(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(start, end);
}

private static IEnumerable<char> alphabetSubsetImplementation(char start, char end)
{
    for (var c = start; c < end; c++)
        yield return c;
}

这样就可以定位,但是问题是,可能错误调用 alphabetSubsetImplementation ,直接使用 他,不是使用 AlphabetSubset2 ,所以在新的C#,可以使用内部方法

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

同时,在异步,如果出现异常,也是难以定位,所以可以用内部方法在异步前判断异常

    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.";
    }
}

在使用异步函数前异常,不让开发者使用没有校验的 longRunningWorkImplementation ,这就是内部方法的使用。

但是可能有兄弟这样写,让我觉得这个语言太垃圾

        public static void A()
        {
            A1();
            void A1()
            {
                void A2()
                {
                    void A3()
                    {

                    }
                }

                A2();
                //A3();
            }

            A1();
        }

改进常量

我们有比较长数字,那么我们在定义需要比较难知道他是否写对

我们可以使用,下划线。这样是分割,可以数我们写了多少数字,可以看我们是不是写错


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

我们还可以定义2进制,原来是无法定义

var b = 0b1010_1011_1100_1101_1110_1111;

这个主要在二进制好,原先的使用true和false,如果还要使用,是用byte转换,如果Iot,我们需要定义二进制,要是使用原的还是难,我就觉得这个功能好。可以分割,我们二进制容易打着就不对,有了分割可以看出没几个,这样看代码简单,这个功能应该是原先就可得到,现在使用_觉得还是没有用。

ref returns 返回值

我们返回的是引用,现在返回可以是值,我们返回数组中的一个值,那么修改这个值,因为放进引用,我们输出数组是修改的值



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);
place = 9; // 修改
WriteLine(array[4]); // 9

全部地方可以支持辣么大

以前支持辣么大的地方很少,关于辣么大,参见 https://docs.microsoft.com/en-us/dotnet/articles/csharp/lambda-expressions

现在可以在所有地方使用辣么大

    // Expression-bodied constructor
public ExpressionMembersExample(string label) => this.Label = label;

private string label;

// Expression-bodied get / set accessors.
public string Label
{
    get => label;
    set => this.label = value ?? "Default label";
}

在很多地方可以扔异常

以前,异常是定义,所以下面代码出错

            private string _name;

        public string Name
        {
            set
            {
                _name = value??throw new ArgumentException();

            }
            get { return Name; }
        }

不能判断 name 空,同时异常。

现在可以

同时可以写

    private ConfigResource loadedConfig = LoadConfigResourceOrDefault() ??
    throw new InvalidOperationException("Could not load config");

广义异步返回类型

以前 Task<>只能在方法使用

    private async Task<int> loadCache()
{
    // simulate async work:
    await Task.Delay(100);
    cache = true;
    cacheResult = 100;
    return cacheResult;
}

现在可以使用 ValueTask<> 返回数值

    public ValueTask<int> CachedFunc()
{
    return (cache) ? new ValueTask<int>(cacheResult) : new ValueTask<int>(loadCache());
}
private bool cache = false;
private int cacheResult;
private async Task<int> loadCache()
{
    // simulate async work:
    await Task.Delay(100);
    cache = true;
    cacheResult = 100;
    return cacheResult;
}

注意使用System.Threading.Tasks.Extension

这个方法可以直接把数值转ValueTask

虽然没有用,和之前的看不出有什么用

            public static async ValueTask<int> ValueTask(int[] numbers)
        {
            if (!numbers.Any())
            {
                return 0;
            }
            else
            {
                return await Task.Run(() => numbers.Sum());
            }
        }

https://docs.microsoft.com/en-us/dotnet/articles/csharp/csharp-7

https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/

VS 17 下载

VS 17 企业版

链接:http://pan.baidu.com/s/1skXDc3z 密码:70d6

如果度盘链接没法使用,请联系我。

代码 https://github.com/alugili/CSharp7Features

本文更新在:http://lindexi.oschina.io/lindexi/post/C-7.0/


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

C# 7.0 特性的更多相关文章

  1. C#3.0 特性

    C#3.0特性 隐式类型的本地变量和数组 对象初始值设定项 集合初始值设定项 扩展方法 匿名类型 lambda表达式 查询关键字 自动实现的属性 分布方法定义 lambda表达式与表达式树 https ...

  2. .NET Standard 2.0 特性介绍和使用指南

    .NET Standard 2.0 发布日期:2017年8月14日 公告原文地址 前言 早上起来.NET社区沸腾了,期待已久的.NET Core 2.0终于发布!根据个人经验,微软的产品一般在2.0时 ...

  3. .Net大局观(2).NET Core 2.0 特性介绍和使用指南

    .NET Core 2.0发布日期:2017年8月14日 前言 这一篇会比较长,系统地介绍了.NET Core 2.0及生态,现状及未来计划,可以作为一门技术的概述来读,也可以作为学习路径.提纲来用. ...

  4. .Net Core 2.0生态(3):ASP.NET Core 2.0 特性介绍和使用指南

    ASP.NET Core 2.0 发布日期:2017年8月14日 ASP.NET团队宣布ASP.NET Core 2.0正式发布,发布Visual Studio 2017 15.3支持ASP.NET ...

  5. .Net Core 2.0生态(4):Entity Framework Core 2.0 特性介绍和使用指南

    前言 这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升级EF也发展到EF6.x,Entity Framework Core是一个支持跨平台的全新版本, ...

  6. .Net Core 2.0 生态(2).NET Core 2.0 特性介绍和使用指南

    .NET Core 2.0发布日期:2017年8月14日 前言 这一篇会比较长,介绍了.NET Core 2.0新特性.工具支持及系统生态,现状及未来计划,可以作为一门技术的概述来读,也可以作为学习路 ...

  7. .Net Core 2.0 生态(1).NET Standard 2.0 特性介绍和使用指南

    .NET Standard 2.0 发布日期:2017年8月14日 公告原文地址 前言 早上起来.NET社区沸腾了,期待已久的.NET Core 2.0终于发布!根据个人经验,微软的产品一般在2.0时 ...

  8. 【EF】Entity Framework Core 2.0 特性介绍和使用指南

    阅读目录 前言 获取和使用 新特性 项目升级和核心API变化 下一步计划 遗憾的地方 回到目录 前言 这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升 ...

  9. C#6.0特性笔记

    Visual Studio 2015的C#6.0,今天无意中看这个视频,怕忘记其中的特性,故此进行记录. public class Point { //Getter专属自动属性 public int ...

  10. C# 3.0 特性之扩展方法

    今天,我们来聊一聊C#的扩展方法. C# 3.0中为我们提供了一个新的特性—扩展方法.什么是扩展方法呢?我们看一下MSDN的注解: 扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型.重新 ...

随机推荐

  1. java可访问修饰符

    修饰符 同一个类中 同一个包中 不同包的子类 不提供包的非子类 private √ friendly(省略) √ √ protected √ √ √ public √ √ √ √

  2. 201521123109《java程序设计》第八周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 1.2 选做:收集你认为有用的代码片段 2. 书面作业 本次作业题集集合 List中指定元素的删除(题目4-1 ...

  3. 201521123037 《Java程序设计》第6周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 1.2 可选:使用常规方法总结其他上课内容. 接口与抽象类 ...

  4. 201521123098 《Java程序设计》 第4周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 1. 学习了继承的基本含义,用"class 子类名 extend 父类名" ...

  5. Java课程设计—学生成绩管理系统(201521123002 林楚虹)

    1. 团队课程设计博客链接 团队博客链接 2.个人责模块或任务说明 根据学生学号查找学生成绩 根据学生姓名(支持模糊匹配)查找学生成绩 用POI技术导出Excel文件 3.自己的代码提交记录截图 4. ...

  6. cityEngine入门(实现数据的预处理以及cityEngine的3维显示)

    一.  实验要求 1.     提供数据: 中田村两个图幅影像数据 DEM提供包含高程数值的文本和矢量数数据 完成内容: 实现中田村两个图幅的拼接,生成一个影像数据(Image.tif) 将DEM数据 ...

  7. MyEclipse中Source Folder,package,folder的区别

    1.在eclipse下,package, source folder, folder都是文件夹. 但它们有区别如: 2. package:当你在建立一个package时,它自动建立到source fo ...

  8. js对象详解(JavaScript对象深度剖析,深度理解js对象)

    js对象详解(JavaScript对象深度剖析,深度理解js对象) 这算是酝酿很久的一篇文章了. JavaScript作为一个基于对象(没有类的概念)的语言,从入门到精通到放弃一直会被对象这个问题围绕 ...

  9. 纯css隐藏移动端滚动条解决方案(ios上流畅滑动)

    纯css隐藏移动端滚动条解决方案(ios上流畅滑动) html代码展示(直接复制代码保存至本地文件运行即可): <!DOCTYPE html> <html lang="en ...

  10. Tomcat配置用户名和密码

    1.在浏览器输入http://localhost:8080/,打开Tomcat自带的默认主页面,右侧有"administration""documentation&quo ...