您当前的位置: 首页 > 

寒冰屋

暂无认证

  • 0浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

C#8的新功能

寒冰屋 发布时间:2019-12-09 23:03:17 ,浏览量:0

目录

 介绍

 C#7.1的新增功能

Async Main

默认文字的更改

元组语法更改

 C#7.2的新增功能

数字文字的前导下划线

私有保护访问修饰符

有条件的“ref”

命名参数的更改

 C#7.3的新增功能

改进stackalloc运算符

固定语句支持更多类型

非托管约束

元组现在支持!=和=

多个编译器选项

 C#8的新增功能

using声明

消费异步流

新型索引(indices)和范围

stackalloc作为表达式

插值字符串的增强

将结构成员设为只读

默认接口方法

局部静态函数

null-coalescing运算符

Switch 表达式变化

可释放的引用结构

 总结

 介绍

C#-在编程世界中众所周知的名称。C#是.NET开发人员的心脏,是一种功能强大的类型安全、复杂、面向对象的语言。它使开发人员能够开发Windows应用程序,Web应用程序,数据库应用程序,Web服务,XML应用程序,后端服务等等。

基本上,C#是由Microsoft在1999年开发的,名称为“C-Like Object Oriented Language”,Microsoft将该语言称为COOL。在2000年7月,该语言被重命名为C#。C#(1.0)的第一个公共版本于2002年1月与Visual Studio 2002一起发布。到目前为止,此语言已经进行了许多更改。每个版本的C#都变得越来越强大。在2018年5月,Microsoft 与Visual Studio 2017一起发布了C#7.3。在此之前,C#7.1和7.2分别在2017年8月和2017年11月发布。最新版本的C#(8.0)于2019年9月发布,与.NET Framework 4.8和Visual Studio 2019兼容。如果要查看C#V7版本的新功能,可以在此处参考我的上一篇文章。

在本文中,我们将看到C#7.1、7.2、7.3和8.0中包含的新功能。因此,让我们开始吧。

 C#7.1的新增功能 Async Main

这是main方法编译技术的巨大变化,现在C#可以接受Async main方法了,它可以帮助开发人员在您的main方法中编写await 代码,请参见以下示例:

static string Main()
{
    await AsyncMethod1();
}

此功能之前,如果你想要把await放到你的async方法中,那么你需要使用GetAwaiter()方法。如果您的main方法返回Task,则它可以包含async修饰符。请参阅以下示例:

static async Task Main()
{
    await AsyncMethod1();
}
默认文字的更改

默认文字会产生要键入的默认值,默认文字的语法有所变化,请参见以下示例:

//previously you wrote
Func whereClause = default(Func);

现在,消除了右侧部分,您只需要键入default关键字,请参见以下示例:

Func whereClause = default;
元组语法更改

元组语法也有所变化,C#7中引入了元组,您需要在程序中定义要使用它的元组的名称,请参见下面的示例:

int count = 3;
string colors = "colors of the flag";
var tupleCol = (count: count, colors: colors);

在这里,我们需要定义count: count和colors: colors名称,但是在C#7.1中,它们更改了如下语法:

int count = 3;
string colors = "colors of the flag";
var tupleCol = (count,colors); // here syntax gets trimmed
 C#7.2的新增功能

在C#7.1版本之后,C#7.2发布了一些高级功能,该版本更加注重效率和性能。让我们找出C#7.2的一些新功能。

数字文字的前导下划线

C#7.0在文字中具有数字分隔符的功能,但是它不接受_(下划线)作为值的字符,但是在C#7.2中,可以使用二进制和十六进制数字文字以下划线开头,请参见以下示例:

int val_bi = 0b_0110_01;
私有保护访问修饰符

在C#7.2中添加了名为'private protected'的新访问修饰符,基本上是private访问修饰符和protected访问修饰符的组合,其中,protected访问修饰符将成员访问限制为仅派生类,而private访问修饰符将成员限制为仅同一类。所以最后,我们得到了一个修饰符,该修饰符将通过包含类或派生类(仅用于同一程序集)来访问。

有条件的“ref”

顾名思义,我们可以根据条件表达式分配'ref'结果,请参见以下示例:

ref var finalVal = ref (value1 != null ? ref val1_arr[0] : ref val2_arr[0]);

在上面的示例中,我们正在分配值给finalVal 变量,但此分配是依赖于with条件运算符,如此val1_arr或var2_arr的任一值将被分配。

命名参数的更改

我们已经在C#7.2中看到了有关函数命名和可选参数的信息,如果参数的顺序正确,则无需为参数分配名称,请参见下面的示例:

//suppose I have written a function and used below named arguments
EmpDetails(EmpID: 3, firstName: "Manavya", City: "Daswel");

//here in C#7.2, I can write the above code line as
EmpDetails(EmpID: 3, "Manavya", City: "Daswel");

//if you observed that firstName: "Manavya" is replaced by only "manavya" 
// and it is the beauty of the C#7.2
 C#7.3的新增功能

此版本更侧重于有效的安全编码,以减少内存消耗,避免错误,缓冲托盘,使现有功能更强大,让我们逐一查找功能。

改进stackalloc运算符

我们知道stackalloc分配一个块,操作符的好处是,它在方法返回退出控件时释放内存,因此不需要垃圾回收。

直到现在,借助stackalloc,您将能够初始化以下值:

int* Array1 = stackalloc int[3] {5, 6, 7};

但从现在开始,数组也可以是以下内容的一部分stackalloc:

Span Array = stackalloc [] {10, 20, 30};
固定语句支持更多类型

现在,固定语句支持更多类型(固定语句是防止垃圾回收器清除可移动变量的语句,您可以使用fixed关键字创建这些语句),因此从C#7.3开始,固定语句支持具有以下内容的任何类型:GetPinnableReference()方法,返回的ref T是固定的。

非托管约束

您可以使用非托管约束来显示类型参数必须是具有非指针的非托管类型(一个成员是一个非托管型如果它是byte,short,sbyte,int,long,char,bool类型)。

unsafe public static byte[] convert_to_byte(this T argument) where T : unmanaged
{
    var size = sizeof(T);
    var result = new Byte[size];
    return result1;
}

上面的示例是非托管和不安全的。

您还可以使用System.Enum和System.Delegate作为基类约束。

元组现在支持!=和=

从C#7.3版本开始,元组现在支持等于和不等于运算符。

众所周知,此等于和不等于运算符比较左侧和右侧的值,请参见以下示例:

var exp1 = (val1: 100, val2: 20);
var exp2 = (val1: 100, val2: 20);
exp1 == exp2; //it will return displays as 'true'

在这里,我们将获得的输出是true,因为两个操作数均为true。

重载的“in”方法的更改

您知道in方法可以很好地替代'out'或'ref'关键字,'in'参数不能通过调用函数进行修改,而'out'或'ref'参数可以通过调用方法进行修改。现在出现问题了,当我们为“按引用”和“按值”方法命名相同的方法时,它会抛出歧义异常,为避免此错误,C#7.3使用了“in”方法,请参见下文样品:

static void calculateArea(var1 arg);
static void calculateArea(in var1 arg);

扩展参数边界

C#7.3具有扩展的out参数范围,现在您可以在构造函数初始值设定项、属性初始值设定项中定义out参数,有关详细信息,请参见下面的示例。

//here, we have defined the out parameter in constructor
public class parent
{
   public parent(int Input, out int Output)
   {
      Output = Input;
   }
}

//now let's use the above class in the following class
public class Child : parent
{
   public Child(int i) : base(Input, out var Output)
   {
      //"The value of 'Output' is " + Output
   }
}
多个编译器选项

C#7.3为您提供了多个编译器选项,您可以使用它们:

  1. 使用公钥签名程序集
  2. 替换构建环境中的路径

使用公钥签名程序集

如果要使用公共密钥对任何程序集进行签名,则可以使用此选项。该程序集将被标识为已签名,但是具有公共密钥,当您要签署开放源代码的程序集时,此选项很有用。语法有点简单,您只需要添加参数作为-public签名即可。

从构建环境替换路径

此选项使您可以用较早的映射源路径替换构建中的源路径,简而言之,它将帮助您使用物理路径映射源路径,语法如下:

-pathmap:path1=sourcePath1,path2=sourcePath2

在这里,path1是源文件的完整路径,path2也是我们需要替换为path1的备用路径。基本上,这些路径可在PDB文件中找到。

 C#8的新增功能

C#8具有非常强大的功能,例如增强了模式匹配,只读成员,static函数(本地),可为空的引用,stackalloc嵌套循环,索引和范围的更改。

让我们一一追踪它们。

using声明

using声明只不过是一种在using关键字之前声明变量的技术,它还告诉编译器应该在方法结束之前(在使用该变量的地方)释放该变量,请参见下面的示例以了解using声明:

var ReadFile(string szpath)
{
 using StreamReader StrFile = new StreamReader(szpath);
 string iCount;
 string szLine;
 while ((szLine = StrFile.ReadLine()) != null) 
  { 
  Console.WriteLine(szLine); 
  iCount++; 
  } 
 // file is disposed here
}

在上面的示例中,您可以看到using语句可以用作声明语句。StrFile一旦达到该方法的闭合括号,便会立即放置该变量。

您可以使用早期的常规编码步骤来写以下相同的代码:

var ReadFile(string szpath)
{
using (StreamReader StrFile = new StreamReader(szpath))
 {
  string iCount;
  string szLine;
  while ((szLine = StrFile.ReadLine()) != null) 
  { 
  Console.WriteLine(szLine); 
  iCount++; 
  } 
 
 } //file disposed here
}

在前面的代码中,我们可以看到StrFile 对象在using 作用域结束后就被释放了。

消费异步流

您是否要使用异步流?那么此功能对您很有用。该任务由一个函数执行,该函数使用IAsyncEnumerable枚举器返回异步流。此方法声明为anync 修饰符,此函数还包含yield return 语句(用于返回异步流)。为了消费anync流,我们需要在实际返回值之前使用await关键字。通过下面的示例,我们在这里返回anync stream。

public async System.Collections.Generic.IAsyncEnumerable asyncStream()
{
    for (int iCount = 0; iCount < 10; iCount++)
    {
        await Task.Delay(10);
        yield return iCount;
    }
}
//call above method in mail method
await foreach (var num in asyncStream())
{
    Console.WriteLine(num);
}

在以上示例中,我们使用async stream最多返回了10 。

新型索引(indices)和范围

C#8带有两种新类型,用于从列表/数组中获取项。System.Index和System.Range用于获取这些类型。^(脱字符)表示索引符号,..表示范围符号。让我们追溯一下它们以了解概念。

详细索引(indices)(^)

通常,当我们从数组中获取项目时,索引以开头0,但是当我们使用这种新类型时,其索引(indices)从结尾开始并向上。

请参见以下示例:

var simArray = new string[]
{                 
    "one",         // 0      ^4
    "two",         // 1      ^3
    "three",       // 2      ^2
    "four",        // 3      ^1
};

在上面的示例中,我们在数组中有四个项,通常,索引从0开始,以3结束,但是对于索引(indices),它从^ 4(结束)到^ 1开始,因此它是保留索引。

现在,如果我尝试获取任何索引,则输出将如下所示:

Console.WriteLine("fetch using simple array " + simArray[1]);
//above code gives output as "two"
Console.WriteLine("fetch using indices caret operator " + simArray[^1]);
//above code gives output as "four"

详细范围(..)

该运算符获取其操作数的开始和结束。

因此,如果我们考虑以上示例并尝试从“simArray”中获取值,那么我们将获得如下输出:

Console.WriteLine("fetch using range operator" + simArray[..]);
//above code gives output as "one" to "four"
Console.WriteLine("fetch using range operator" + simArray[..3]);
//above code gives output as "one" to "three"
Console.WriteLine("fetch using range operator" + simArray[2..]);
//above code gives output as "two" to "four"

在上面的示例中:

  • [..]表示,获取列表的所有项
  • [..3]表示。提取所有项目直到索引3
  • [1 ..]表示从索引1开始提取所有项
stackalloc作为表达式

我们知道stackalloc操作符会在堆栈上分配一个块内存,并在方法调用退出时释放,因此不需要垃圾回收。stackalloc的语法为:

stackalloc T[E]

其中T是非托管类型,E是类型int的表达式。

在C#8中,您可以将其stackalloc用作表达式,如果返回结果System.Span,则适用于该表达式。请参见以下示例:

Span num = stackalloc[] { 20, 30, 40 };
var index = num.IndexOfAny(stackalloc[] { 20 });
Console.WriteLine(index);  // output: 0

在以上示例中,我们已将stackalloc用作数据int的集合,并将其stackalloc作为表达式传递给IndexOfAny方法,方法将返回0。

插值字符串的增强

C#8在插值string中引入了一些增强功能。

你知道插值string什么吗?

$用于将string识别为插值string,这string包含插值表达式,并进一步由实际结果代替。请参见以下示例:

string szName = "ABC";
int iCount = 15;

// String interpolation:
Console.WriteLine($"Hello, {szName}! You have {iCount} apples");

如果我们在上面的代码上运行,则输出将为“ Hello, ABC! You have 15 apples”,因此可以说花括号中的变量被实际的string替换。(在这里,如果您观察到可以看到string以$开头,这样的string则称为插值string。

现在在C#8中,$@""或者@$""有效表示您可以通过@或$来开始string。在早期版本的C#中,@仅在$之后被允许。

将结构成员设为只读

如果要为任何struct成员提供只读属性,则可以在C#8中使用,现在可以将struct成员设为只读,而不是将整个struct成员设置为只读。该readonly属性指示它不会修改状态,请参见下面的示例代码片段,这是我们遵循的将整个结构设为只读的通用代码实践:

public readonly struct getSqure
{
    public int InputA { get; set; }
    public int InputB { get; set; }
    public int output => Math.Pow(A,B);

    public override string ToString() =>
        $"The answer is : {output} ";
}

在上面的示例中,我们使用了Math.Pow方法来计算输入数字的平方,而不是在C#8中,我们可以将struct成员设为只读,请参见下面的代码片段,其中将一些struct成员设为readonly。

 

public struct getSqure
{
    public int InputA { get; set; }
    public int InputB { get; set; }
    public readonly int output => Math.Pow(A,B);

    public override string ToString() =>
        $"The answer is : {output} ";
}
默认接口方法

这是C#8的一个好功能,即使在以后的版本中,它也允许您在接口中添加成员(如方法),而不会违反现有的实现,因为在这里,现有的实现会继承默认实现。

局部静态函数

在C#8中,可以将局部函数设为static函数,这有助于证明局部函数不包含内部封闭循环中的变量,请参见下面的示例中试图访问局部变量的局部函数。

int Add()
{
    int A;
    Add();
    return Sum;

    void LocalFunction() => A + 3;
}

现在,为了避免此类问题,我们可以使用局部static函数,其中可以使用方法重新编写相同的代码:

int Add()
{
    int A = 5;
    return Sum(A);

    static int Sum(int val) => val + 3;
}
null-coalescing运算符

C#8引入了称为null-coalescing运算符的运算符。该运算符表示为??=。基本上,此运算符用于将右侧表达式的值分配给右侧操作数,但是当左侧操作数的值计算为null时,此分配是可能的。简单来说,此运算符将返回左侧操作数的值,如果不是null,否则将返回右侧操作数的值,有关更多详细信息,请参见下面的代码段:

List lstNum = null;
int? a = null;

(lstNum ??= new List()).Add(100);
Console.WriteLine(string.Join(" ", lstNum)); 

// the output would be : 100

lstNum.Add(a ??= 0);
Console.WriteLine(string.Join(" ", numbers));  // output: 100 0
// the output would be : 100 0
Switch 表达式变化

switch用于模式匹配的表达式及其语法有所变化,请参见下面的示例。

假设我们有一个带有一些随机数的enum,如下所示:

public enum RandomNum
{
    One,
    Three,
    Five,
    Seven,
    Six,
}

现在,您可以通过以下方式使用switch表达式:

public static string getRandomNum(RandomNum iNum) =>
    iNum switch
    {
        RandomNum.One => return "1",
        RandomNum.Three => return "3",
        RandomNum.Five => return "5",
        RandomNum.Seven => return "7",
        RandomNum.Six => return "6",
        _              => throw new Exception("invalid number value"),
    };

在上面的switch表达式示例中,您将发现语法级别更改如下:

  • 在原始语法中,case关键字与switch语句一起使用,但此处case 关键字由=>代替,这更易于说明。
  • 在原始语法中,default 关键字用于默认情况,但在这里,默认关键字由_(下划线)符号代替。
  • 在原始语法中,switch关键字在变量之前使用,但是在这里,它在变量之后使用。
可释放的引用结构

声明为ref 的struct 无权实现任何接口,因此我们无权处置其对象,因为它无权实现IDisposable 接口,因此我们必须使我们需要使用可释放的ref结构来访问void Dispose()方法。同样,我们也可以配置readonly ref结构。

 总结

在本文中,我们看到了C#7.1、7.2、7.3和8.0的不同功能,每个版本都有其独特的功能使C#比以前的版本更强大。在本文的更高版本中,我们将详细介绍每个功能,然后,您才能享受本文的乐趣。

关注
打赏
1665926880
查看更多评论
立即登录/注册

微信扫码登录

0.0465s