C# 7.0 特性
在昨天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 特性的更多相关文章
- C#3.0 特性
C#3.0特性 隐式类型的本地变量和数组 对象初始值设定项 集合初始值设定项 扩展方法 匿名类型 lambda表达式 查询关键字 自动实现的属性 分布方法定义 lambda表达式与表达式树 https ...
- .NET Standard 2.0 特性介绍和使用指南
.NET Standard 2.0 发布日期:2017年8月14日 公告原文地址 前言 早上起来.NET社区沸腾了,期待已久的.NET Core 2.0终于发布!根据个人经验,微软的产品一般在2.0时 ...
- .Net大局观(2).NET Core 2.0 特性介绍和使用指南
.NET Core 2.0发布日期:2017年8月14日 前言 这一篇会比较长,系统地介绍了.NET Core 2.0及生态,现状及未来计划,可以作为一门技术的概述来读,也可以作为学习路径.提纲来用. ...
- .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 ...
- .Net Core 2.0生态(4):Entity Framework Core 2.0 特性介绍和使用指南
前言 这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升级EF也发展到EF6.x,Entity Framework Core是一个支持跨平台的全新版本, ...
- .Net Core 2.0 生态(2).NET Core 2.0 特性介绍和使用指南
.NET Core 2.0发布日期:2017年8月14日 前言 这一篇会比较长,介绍了.NET Core 2.0新特性.工具支持及系统生态,现状及未来计划,可以作为一门技术的概述来读,也可以作为学习路 ...
- .Net Core 2.0 生态(1).NET Standard 2.0 特性介绍和使用指南
.NET Standard 2.0 发布日期:2017年8月14日 公告原文地址 前言 早上起来.NET社区沸腾了,期待已久的.NET Core 2.0终于发布!根据个人经验,微软的产品一般在2.0时 ...
- 【EF】Entity Framework Core 2.0 特性介绍和使用指南
阅读目录 前言 获取和使用 新特性 项目升级和核心API变化 下一步计划 遗憾的地方 回到目录 前言 这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升 ...
- C#6.0特性笔记
Visual Studio 2015的C#6.0,今天无意中看这个视频,怕忘记其中的特性,故此进行记录. public class Point { //Getter专属自动属性 public int ...
- C# 3.0 特性之扩展方法
今天,我们来聊一聊C#的扩展方法. C# 3.0中为我们提供了一个新的特性—扩展方法.什么是扩展方法呢?我们看一下MSDN的注解: 扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型.重新 ...
随机推荐
- 201521123072《Java程序设计》第6周学习总结
201521123072<Java程序设计>第6周学习总结 标签(空格分隔): java 1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画 ...
- 201521123005《java程序设计》第三周学习总结
1. 本周学习总结 初学面向对象,会学习到很多碎片化的概念与知识.使用思维导图将这些碎片化的概念.知识组织起来.使用工具画出本周学习到的知识点. 参考资料: 百度脑图 XMind 2. 书面作业 ·Q ...
- Java 第九周总结
1. 本周学习总结 2. 书面作业 1.常用异常 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代码中经常出现什么异常.需要捕获吗(为什么)?应如何避免? 以前的代码经常出现空指针的,需 ...
- 201521123024 《java程序设计》 第12周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容.
- Python接口测试自动化说明及代码实例:含get、post、put、delete等方法
一.接口说明文档 环境准备: 安装火狐 安装插件: httprequester https://addons.mozilla.org/en-US/firefox/addon/httprequester ...
- hibernate中Query的list和iterator区别(续)
打开cache后query的list和iterator方法区别 将query 的cache打开的话,缓存的是query本身,以hql 生成的 sql ,再加上参数,分页等信息做为key值,而不是que ...
- java面试之Hashmap
在java面试中hashMap应该说一个必考的题目,而且HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接 ...
- 【BBED】BBED模拟并修复ORA-08102错误
[BBED]BBED模拟并修复ORA-08102错误 1.1 BLOG文档结构图 1.2 前言部分 1.2.1 导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其 ...
- jquery实现点击div外隐藏div
html <div style="width:100px;height:100px;border:1px solid #ff0" id="div"> ...
- React获得真实的DOM操作
真实的DOM操作 ------------------------------------------------------------------------------------------- ...