cnblogs/dcrenl/Visual Studio 2017 新特性.html
2024-09-24 12:43:01 +08:00

377 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<h2>1. out-variables(Out变量)</h2>
<p>以前,我们使用out变量的时候,需要在外部先申明,然后才能传入方法,类似如下:</p>
<div>
<pre>string ddd = ""; //先申明变量
ccc.StringOut(out ddd);
Console.WriteLine(ddd);
</pre>
</div>
<p>在C#7.0中我们可以不必申明,直接在参数传递的同时申明它,如下:</p>
<div>
<pre> StringOut(out string ddd); //传递的同时申明
Console.WriteLine(ddd);
Console.ReadLine();
</pre>
</div>
<p>&nbsp;</p>
<h2>2.Tuples(元组)</h2>
<p>曾今在.NET4.0中,微软对多个返回值给了我们一个解决方案叫元组,类似代码如下:</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre> static void Main(string[] args)
{
var data = GetFullName();
Console.WriteLine(data.Item1);
Console.WriteLine(data.Item2);
Console.WriteLine(data.Item3);
Console.ReadLine();
}
static Tuple&lt;&lt;span style="font-family: "Courier New" !important; line-height: 1.8; margin: 0px; padding: 0px; color: rgb(0, 0, 255);"&gt;string, string, string&gt; GetFullName()
{
return new Tuple&lt;&lt;span style="font-family: "Courier New" !important; line-height: 1.8; margin: 0px; padding: 0px; color: rgb(0, 0, 255);"&gt;string, string, string&gt;("a", "b", "c");
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>上面代码展示了一个方法,返回含有3个字符串的元组,然而当我们获取到值,使用的时候 心已经炸了,Item1,Item2,Item3是什么鬼,虽然达到了我们的要求,但是实在不优雅</p>
<p>那么,在C#7.0中,微软提供了更优雅的方案:(注意:需要通过<strong>nuget</strong>引用System.ValueTuple)如下:</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre> static void Main(string[] args)
{
var data=GetFullName();
Console.WriteLine(data.a); //可用命名获取到值
Console.WriteLine(data.b);
Console.WriteLine(data.c);
Console.ReadLine();
}
//方法定义为多个返回值,并命名
private static (string a,string b,string c) GetFullName()
{
return ("a","b","c");
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>解构元组,有的时候我们不想用var匿名来获取,那么如何获取abc呢?我们可以如下:</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre> static void Main(string[] args)
{
//定义解构元组
(string a, string b, string c) = GetFullName();
Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
Console.ReadLine();
}
private static (string a,string b,string c) GetFullName()
{
return ("a","b","c");
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>&nbsp;</p>
<h2>3.&nbsp;Pattern Matching(匹配模式)</h2>
<p>在C#7.0中,引入了匹配模式的玩法,先举个老栗子.一个object类型,我们想判断他是否为int如果是int我们就加10,然后输出,需要如下:</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre>object a = 1;
if (a is int) //is判断
{
int b = (int)a; //拆
int d = b+10; //加10
Console.WriteLine(d); //输出
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>那么在C#7.0中,首先就是对is的一个小扩展,我们只需要这样写就行了,如下:</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre>object a = 1;
if (a is int c) //这里,判断为int后就直接赋值给c
{
int d = c + 10;
Console.WriteLine(d);
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>这样是不是很方便?特别是经常用反射的同志们..</p>
<p>那么问题来了,挖掘机技术哪家强?!(咳咳,呸 开玩笑)</p>
<p>其实是,如果有多种类型需要匹配,那怎么办?多个if else?当然没问题,不过,微软爸爸也提供了<code title="Sum using switch">switch的新玩法,我们来看看,如下:</code></p>
<p>我们定义一个Add的方法,以Object作为参数,返回动态类型</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre> static dynamic Add(object a)
{
dynamic data;
switch (a)
{
case int b:
data=b++;
break;
case string c:
data= c + "aaa";
break;
default:
data = null;
break;
}
return data;
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>下面运行,传入int类型:</p>
<div>
<pre>object a = 1;
var data= Add(a);
Console.WriteLine(data.GetType());
Console.WriteLine(data);
</pre>
</div>
<p>输出如图:<img src="http://images2015.cnblogs.com/blog/653851/201703/653851-20170309133419906-1465823876.png" alt="" /></p>
<p>我们传入String类型的参数,代码和输出如下:</p>
<div>
<pre>object a = "bbbb";
var data= Add(a);
Console.WriteLine(data.GetType());
Console.WriteLine(data);
</pre>
</div>
<p><img src="http://images2015.cnblogs.com/blog/653851/201703/653851-20170309133532922-346784651.png" alt="" /></p>
<p>通过如上代码,我们就可以体会到switch的新玩法是多么的顺畅和强大了.</p>
<p><strong>匹配模式的Case When筛选</strong></p>
<p>有的基友就要问了.既然我们可以在Switch里面匹配类型了,那我们能不能顺便筛选一下值?答案当然是肯定的.</p>
<p>我们把上面的Switch代码改一下,如下:</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre> switch (a)
{
case int b when b &lt; 0:
data = b + 100;
break;
case int b:
data=b++;
break;
case string c:
data= c + "aaa";
break;
default:
data = null;
break;
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>在传入-1试试,看结果如下:</p>
<p><img src="http://images2015.cnblogs.com/blog/653851/201703/653851-20170309134150578-633941395.png" alt="" /></p>
<p>&nbsp;&nbsp;</p>
<h2>4.ref&nbsp;locals and returns(<strong>局部变量</strong><strong></strong><strong>引用返回)</strong></h2>
<p>首先我们知道 ref关键字是将值传递变为引用传递</p>
<p>那么我们先来看看<strong>ref locals(ref局部变量)</strong></p>
<p>列子代码如下:</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre> static void Main(string[] args)
{
int x = 3;
ref int x1 = ref x; //注意这里,我们通过ref关键字 把x赋给了x1
x1 = 2;
Console.WriteLine($"改变后的变量 {nameof(x)} 值为: {x}");
Console.ReadLine();
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p><strong>这段代码最终输出 "2"</strong></p>
<p>大家注意注释的部分,我们通过ref关键字把x赋给了x1,如果是值类型的传递,那么对x将毫无影响 还是输出3.</p>
<p>好处不言而喻,在某些特定的场合,我们可以直接用ref来引用传递,减少了值传递所需要开辟的空间.</p>
<p>&nbsp;</p>
<p>接下来我们看看<strong>ref &nbsp;returns&nbsp;(ref引用返回)</strong></p>
<p>这个功能其实是非常有用的,我们可以把值类型当作引用类型来进行return</p>
<p>老规矩,我们举个栗子,代码如下:</p>
<p>很简单的逻辑..获取指定数组的指定下标的值</p>
<div>
<pre>static ref int GetByIndex(int[] arr, int ix) =&gt; ref arr[ix]; //获取指定数组的指定下标
</pre>
</div>
<p>我们编写测试代码如下:</p>
<div>
<pre> int[] arr = { 1, 2, 3, 4, 5 };
ref int x = ref GetByIndex(arr, 2); //调用刚才的方法
x = 99;
Console.WriteLine($"数组arr[2]的值为: {arr[2]}");
Console.ReadLine();
</pre>
</div>
<p>我们通过ref返回引用类型,在重新赋值, arr数组中的值,相应也改变了.</p>
<p>&nbsp;</p>
<h2>5.Local Functions (局部函数)</h2>
<p>嗯,这个就有点颠覆..大家都知道,<strong>局部变量</strong>是指:<strong>只在特定过程或函数中可以访问的变量。</strong></p>
<p>那这个<strong>局部函数</strong>,顾名思义:<strong>只在特定的函数中可以访问的函数(妈蛋 好绕口)</strong></p>
<p>使用方法如下:</p>
<p>&nbsp;</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre> public static void DoSomeing()
{
//调用Dosmeing2
int data = Dosmeing2(100, 200);
Console.WriteLine(data);
//定义局部函数,Dosmeing2.
int Dosmeing2(int a, int b)
{
return a + b;
}
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>呃,解释下来 大概就是在DoSomeing中定义了一个DoSomeing2的方法,..在前面调用了一下.<strong>(注:值得一提的是局部函数定义在方法的任何位置,都可以在方法内被调用,不用遵循逐行解析的方式)</strong></p>
<p>&nbsp;&nbsp;</p>
<h2>6.More expressio n-bodied members(更多的函数成员的表达式体)</h2>
<div>
<p>C#6.0中,提供了对于只有一条语句的方法体可以简写成表达式。</p>
<p>如下:</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre> public void CreateCaCheContext() =&gt; new CaCheContext();
//等价于下面的代码
public void CreateCaCheContext()
{
new CaCheContext();
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>但是,并不支持用于构造函数,析构函数,和属性访问器,那么C#7.0就支持了..代码如下:</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre>// 构造函数的表达式写法
public CaCheContext(string label) =&gt; this.Label = label;
// 析构函数的表达式写法
~CaCheContext() =&gt; Console.Error.WriteLine("Finalized!");
private string label;
// Get/Set属性访问器的表达式写法
public string Label
{
get =&gt; label;
set =&gt; this.label = value ?? "Default label";
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<h2>&nbsp;</h2>
<h2>7. t h r o w E x p r e s s i o n s(异常表达式)</h2>
<h2>&nbsp;</h2>
<p>在C#7.0以前,我们想判断一个字符串是否为null,如果为null则抛除异常,我们需要这么写:</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre> public string IsNull()
{
string a = null;
if (a == null)
{
throw new Exception("异常了!");
}
return a;
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>这样,我们就很不方便,特别是在三元表达式 或者非空表达式中,都无法抛除这个异常,需要写if语句.</p>
<p>那么我们在C#7.0中,可以这样:</p>
<div>
<pre> public string IsNull()
{
string a = null;
return a ?? throw new Exception("异常了!");
}
</pre>
</div>
<p>&nbsp;</p>
<h2>8.Generalized async return types (通用异步返回类型)</h2>
<p>嗯,这个,怎么说呢,其实我异步用的较少,所以对这个感觉理解不深刻,还是觉得然并卵,在某些特定的情况下应该是有用的.</p>
<p>我就直接翻译官方的原文了,实例代码也是官方的原文.</p>
<p>异步方法必须返回 voidTask 或 Task,这次加入了新的ValueTask,来防止异步运行的结果在等待时已可用的情境下,对 Task 进行分配。对于许多示例中设计缓冲的异步场景,这可以大大减少分配的数量并显著地提升性能。</p>
<p>官方的实例展示的主要是意思是:一个数据,在已经缓存的情况下,可以使用ValueTask来返回异步或者同步2种方案</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre> public class CaCheContext
{
public ValueTask&lt;&lt;span style="font-family: "Courier New" !important; line-height: 1.8; margin: 0px; padding: 0px; color: rgb(0, 0, 255);"&gt;int&gt; CachedFunc()
{
return (cache) ? new ValueTask&lt;&lt;span style="font-family: "Courier New" !important; line-height: 1.8; margin: 0px; padding: 0px; color: rgb(0, 0, 255);"&gt;int&gt;(cacheResult) : new ValueTask&lt;&lt;span style="font-family: "Courier New" !important; line-height: 1.8; margin: 0px; padding: 0px; color: rgb(0, 0, 255);"&gt;int&gt;(loadCache());
}
private bool cache = false;
private int cacheResult;
private async Task&lt;&lt;span style="font-family: "Courier New" !important; line-height: 1.8; margin: 0px; padding: 0px; color: rgb(0, 0, 255);"&gt;int&gt; loadCache()
{
// simulate async work:
await Task.Delay(5000);
cache = true;
cacheResult = 100;
return cacheResult;
}
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>调用的代码和结果如下:</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre> //main方法可不能用async修饰,所以用了委托.
static void Main(string[] args)
{
Action act = async () =&gt;
{
CaCheContext cc = new CaCheContext();
int data = await cc.CachedFunc();
Console.WriteLine(data);
int data2 = await cc.CachedFunc();
Console.WriteLine(data2);
};
// 调用委托
act();
Console.Read();
}
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>上面的代码,我们连续调用了2次,第一次,等待了5秒出现结果.第二次则没有等待直接出现结果和预期的效果一致.</p>
<p>&nbsp;</p>
<h2>9.Numeric literal syntax improvements(数值文字语法改进)</h2>
<p>在C#7.0中,允许数字中出现"_"这个分割符号.来提高可读性,举例如下:</p>
<div>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
<pre> int a = 123_456;
int b = 0xAB_CD_EF;
int c = 123456;
int d = 0xABCDEF;
Console.WriteLine(a==c);
Console.WriteLine(b==d);
//如上代码会显示两个true,在数字中用"_"分隔符不会影响结果,只是为了提高可读性
</pre>
<div><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码" /></div>
</div>
<p>当然,既然是数字类型的分隔符,那么&nbsp;<strong><code>decimal</code>,&nbsp;<code>float</code>&nbsp;&nbsp;</strong><code><strong>double</strong>&nbsp;&nbsp;都是可以这样被分割的..</code></p>
</div>