美文网首页
C# 6/7 新功能

C# 6/7 新功能

作者: 165e0c0b59d2 | 来源:发表于2017-08-25 14:03 被阅读0次

C#新功能

一、C#历史演变

C# 1,Visual Studio .NET 2002:

  • C# 初版。

C# 1.1,Visual Studio .NET 2003:

  • /#line 杂注和 xml 文档注释。

C# 2,Visual Studio .NET 2005:

  • 匿名方法、泛型、可空类型、迭代器/yield、static 类、委托的协变和逆变。

C# 3,Visual Studio .NET 2008:

  • 对象和集合初始值设定项、lambda 表达式、扩展方法、匿名类型、自动属性、本地 var 类型推理和语言集成查询 (LINQ)。

C# 4,Visual Studio .NET 2010:

  • Dynamic、命名实参和可选实参、泛型协变和逆变。

C# 5,Visual Studio .NET 2012:

  • Async / await 和调用方信息特性

二、C# 6 的新功能 (Visual Studio .NET 2015)

1. 扩展自动属性语法

自动属性初始化表达式。
    public class Example
    {
        // 6.0新增语法糖:属性初始化表达式
        public string FirstName { get; set; } = "Monkey";
    
        // 6.0之前的写法
        private string _firstName = "Monkey";
        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }
    }
只读自动属性(Read-only auto-properties)

只能在<font color=red >构造函数</font>或<font color=red >属性初始化表达式</font>中设置属性

public class Example
{
    public string FirstName { get; } = "Monkey";

    public string LastName { get; } 

    public Example(string lastName)
    {
        LastName = lastName;
    }
}

2. 表达式主体(Expression-bodied function members)

通过表达式主体定义,可采用非常简洁的可读形式提供成员的实现
语法:

member => expression;

C#6支持:属性Get、方法

    //方法的表达式主体定义
    private static string SayHello() => "Hello World";
    //属性Get的表达式主体定义
    public string FullName => $"{FirstName} {LastName}";

3.导入静态类 (using static)

允许访问类型的静态成员,而无需限定使用类型名称进行访问:
r

using staticSystem.String

if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

注意:扩展方法<font color=red >不能</font>使用此方式

using static System.Linq.Enumerable;
public bool MakesDeansList()
{
    return Grades.All(g => g > 3.5) && Grades.Any();
    // Code below generates CS0103: 
    // The name 'All' does not exist in the current context.
    //return All(Grades, g => g > 3.5) && Grades.Any();
}

4.Null 条件运算符

成员访问 (?.) :
如果value==null,则返回null

public static string Truncate(string value, int length)
{
    /
    return value?.Substring(0, Math.Min(value.Length, length));

    // C# 6.0 之前的写法
    //string result = value;
    //if (value != null)
    //{
    //    result = value.Substring(0, Math.Min(value.Length, length));
    //}
    //return result;
}

索引 (?[) 操作:

List<Example> examples = null;
Example example = examples?[0]; 
// 上述相当于 Example? item = (examples != null) ? examples[0] : null

Console.WriteLine(example == null); // 输出 True

5.字符串插值 (String Interpolation)

public string GetFormattedGradePoint() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average()}";

public string GetGradePointPercentage() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";

public string GetGradePointPercentages() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Any() ? Grades.Average() : double.NaN:F2}";

public string GetGradePointPercentages() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {(Grades.Any() ? Grades.Average() : double.NaN):F2}";

public string GetAllGrades() =>
    $@"All Grades: {Grades.OrderByDescending(g => g)
    .Select(s => s.ToString("F2")).Aggregate((partial, element) => $"{partial}, {element}")}";

6.异常过滤器(Exception Filters)

catch (ArgumentNullException e) when (e.ParamName == “…”)  
{  
}

如果括号表达式(when)的结果为 true 时,才执行对应 catch 块中的语句,否则继续搜索处理程序。

使用场景:http处理

public static async Task<string> MakeRequestWithNotModifiedSupport()
{ 
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        return "Site Moved";
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("304"))
    {
        return "Use the Cache";
    }
}

7.nameof表达式

使用场景:获取错误参数名

if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

使用场景:使用INotifyPropertyChanged接口监听属性变化。

public string LastName
{
    get { return lastName; }
    set
    {
        if (value != lastName)
        {
            lastName = value;
            PropertyChanged?.Invoke(this, 
                new PropertyChangedEventArgs(nameof(LastName)));
        }
    }
}
private string lastName;

注意: NameOf只会返回Member的字符串,如果前面有对象或者命名空间,NameOf只会返回 . 的最后一部分, 另外NameOf有很多情况是不支持的,比如方法,关键字,对象的实例以及字符串和表达式

8.在 catch 和 finally 块使用关键字 await

public static async Task<string> MakeRequestAndLogFailures()
{ 
    await logMethodEntrance();
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        await logError("Recovered from redirect", e);
        return "Site Moved";
    }
    finally
    {
        await logMethodExit();
        client.Dispose();
    }
}

9.索引初始化器(Index Initializers)

private List<string> messages = new List<string> 
{
    "Page not Found",
    "Page moved, but left a forwarding address.",
    "The web server can't come out to play today."
};
private Dictionary<int, string> webErrors = new Dictionary<int, string>
{
    [404] = "Page not Found",
    [302] = "Page moved, but left a forwarding address.",
    [500] = "The web server can't come out to play today."
};

三、C# 7 的新功能(Visual Studio .NET 2017)

1.out 变量

//之前版本

int numericResult;
if (int.TryParse(input, out numericResult))
    WriteLine(numericResult);
else
    WriteLine("Could not parse input");

C# 7在参数列表中声明变量


if (int.TryParse(input, out int result))
    WriteLine(result);
else
    WriteLine("Could not parse input");

2.元组(Tuples)

需要引用包:System.ValueTuple

元组是轻量级数据结构,包含多个字段来表示数据成员

var unnamed = ("one", "two");//字段名称为 Item1、Item2、Item3......

var named = (first: "one", second: "two");

(string Alpha, string Beta) namedLetters = ("a", "b");

var alphabetStart = (Alpha: "a", Beta: "b");

元组最适合作为private和internal方法的返回类型,如 分页结果集中的数据和总行数

private static (int Max, int Min) Range(IEnumerable<int> numbers)
{
    int min = int.MaxValue;
    int max = int.MinValue;
    foreach(var n in numbers)
    {
        min = (n < min) ? n : min;
        max = (n > max) ? n : max;
    }
    return (max, min);
}

参考:https://docs.microsoft.com/zh-cn/dotnet/csharp/tuples

3.丢弃(Discards)

从 C# 7 开始,C# 支持放弃,这是一种在应用程序代码中人为取消使用的临时虚拟变量。 放弃相当于未赋值的变量;它们没有值。 因为只有一个放弃变量,并且甚至不为该变量分配存储空间,所以放弃可减少内存分配。

场景1:元组和对象析1

var (_, _, area) = city.GetCityInformation(cityName);
public class Example
{
   public static void Main()
   {
       Person p = new Person("John", "Quincy", "Adams", "Boston", "MA");

       // <Snippet1>
       // Deconstruct the person object.
       var (fName, _, city, _) = p;
       Console.WriteLine($"Hello {fName} of {city}!");
       // The example displays the following output:
       //      Hello John of Boston!       
       // </Snippet1>
   }
}

场景2:使用 out 参数

if (DateTime.TryParse(dateString, out _)) 
            Console.WriteLine($"'{dateString}': valid");

场景3:独立丢弃

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      ExecuteAsyncMethods().Wait();
   }

   private static async Task ExecuteAsyncMethods()
   {    
      Console.WriteLine("About to launch a task...");
      _ = Task.Run(() => { var iterations = 0;  
                           for (int ctr = 0; ctr < int.MaxValue; ctr++)
                              iterations++;
                           Console.WriteLine("Completed looping operation...");
                           throw new InvalidOperationException();
                         });
      await Task.Delay(5000);                        
      Console.WriteLine("Exiting after 5 second delay");
   }
}
// The example displays output like the following:
//       About to launch a task...
//       Completed looping operation...
//       Exiting after 5 second delay

场景4:使用 switch 和 is 的模式匹配

参考:https://docs.microsoft.com/zh-cn/dotnet/csharp/discards

4.模式匹配(Pattern Matching)

is 类型模式表达式:优化原有语法

public static double ComputeAreaModernIs(object shape)
{
    if (shape is Square s)
        return s.Side * s.Side;
    else if (shape is Circle c)
        return c.Radius * c.Radius * Math.PI;
    else if (shape is Rectangle r)
        return r.Height * r.Length;
    // elided
    throw new ArgumentException(
        message: "shape is not a recognized shape",
        paramName: nameof(shape));
}

编写 if (!(shape is Square s)) 来反转逻辑,变量 s 会只在 false 分支中进行明确赋值。

使用模式匹配 switch 语句

public static double ComputeAreaModernSwitch(object shape)
{
    switch (shape)
    {
        case Square s:
            return s.Side * s.Side;
        case Circle c:
            return c.Radius * c.Radius * Math.PI;
        case Rectangle r:
            return r.Height * r.Length;
        default:
            throw new ArgumentException(
                message: "shape is not a recognized shape",
                paramName: nameof(shape));
    }
}

case 表达式中的 when 语句

public static double ComputeArea_Version5(object shape)
{
    switch (shape)
    {
        case Square s when s.Side == 0:
        case Circle c when c.Radius == 0:
        case Triangle t when t.Base == 0 || t.Height == 0:
        case Rectangle r when r.Length == 0 || r.Height == 0:
            return 0;

        case Square s:
            return s.Side * s.Side;
        case Circle c:
            return c.Radius * c.Radius * Math.PI;
        case Triangle t:
            return t.Base * t.Height * 2;
        case Rectangle r:
            return r.Length * r.Height;
//重点
        case null:
            throw new ArgumentNullException(paramName: nameof(shape), message: "Shape must not be null");
        default:
            throw new ArgumentException(
                message: "shape is not a recognized shape",
                paramName: nameof(shape));
    }
}

参考:https://docs.microsoft.com/zh-cn/dotnet/csharp/pattern-matching

5.ref 返回值和局部变量(ref locals and returns)

引用返回值允许方法将对象的引用(而不是值)返回给调用方。 然后,调用方可以选择将返回的对象视为按值返回或按引用返回。 如果按引用返回的值被调用方处理为引用(而非值),则该值即为 ref 局部变量。

示例

FindNumber 方法按引用返回第一个大于或等于作为参数传递的数字的数字。如果没有大于或等于该参数的数字,则方法返回索引 0 中的数字。

using System;

class NumberStore
{
   int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };

   public ref int FindNumber(int target)
   {
      for (int ctr = 0; ctr < numbers.Length; ctr++) {
         if (target == numbers[ctr]) {
            return ref numbers[ctr];
         }   
         else if (ctr == numbers.Length - 1) {
            return ref numbers[ctr];
         }      
         else if (target < numbers[ctr]) {
            if (ctr > 0 && target > numbers[ctr - 1])
               return ref numbers[ctr];
            else if (ctr == 0)
               return ref numbers[0];      
         }
      }
      return ref numbers[0];
   }

   public override string ToString()
   {
      string retval = "";
      for (int ctr = 0; ctr < numbers.Length; ctr++) {
         retval += $"{numbers[ctr]} ";   
      }
      return retval.Trim();   
   }
}

如示例中的输出所示,此更改将反映在 NumberStore 实例的数组元素的值中。

using System;

public class Example
{   
   static void Main(string[] args)
   {
      var store = new NumberStore();
      Console.WriteLine($"Original sequence: {store.ToString()}");
      int number = 16;
      ref var value = ref store.FindNumber(number);
      value*=2;
      Console.WriteLine($"New sequence:      {store.ToString()}");
   }
}
// The example displays the following output:
//       Original sequence: 1 3 7 15 31 63 127 255 511 1023
//       New sequence:      1 3 7 15 62 63 127 255 511 1023

参考:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/ref-returns

6.Local Functions

从 C# 7 开始,C# 支持本地函数。 本地函数是一种嵌套在另一成员中的类型的私有方法。 仅能从其包含成员中调用它们。 可以在以下位置中声明和调用本地函数:

  • 方法(尤其是迭代器方法和异步方法)
  • 构造函数
  • 属性访问器
  • 事件访问器
  • 匿名方法
  • Lambda 表达式
  • 终结器
  • 其他本地函数
using System;
using System.IO;

class Example
{
    static void Main()
    {
        string contents = GetText(@"C:\temp", "example.txt");
        Console.WriteLine("Contents of the file:\n" + contents);
    }
   
    private static string GetText(string path, string filename)
    {
         var sr = File.OpenText(AppendPathSeparator(path) + filename);
         var text = sr.ReadToEnd();
         return text;
         
         // Declare a local function.
         string AppendPathSeparator(string filepath)
         {
            if (! filepath.EndsWith(@"\"))
               filepath += @"\";

            return filepath;   
         }
    } 
}

参考:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/local-functions

7.更多的表达式主体成员(More expression-bodied members)

C#7支持:构造函数,终结器,属性 Set 语句,索引器

构造函数

public class Location
{
   private string locationName;
   
   public Location(string name) => locationName = name;
}

终结器

using System;

public class Destroyer
{
   public override string ToString() => GetType().Name;
   
   ~Destroyer() => Console.WriteLine($"The {ToString()} destructor is executing.");
}

属性 Set 语句,

public class Location
{
   public string Name
   {
      get => locationName;
      set => locationName = value;
   } 
}

索引器

using System;
using System.Collections.Generic;

public class Sports
{
   private string[] types = { "Baseball", "Basketball", "Football", 
                              "Hockey", "Soccer", "Tennis", 
                              "Volleyball" }; 

   public string this[int i]
   {
      get => types[i];
      set => types[i] = value;
   }
}

参考:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/statements-expressions-operators/expressions#expression-body-definitions

8.throw 表达式(throw Expressions)

从 C# 7 开始,throw 可以用作表达式和语句。 这允许在以前不支持的上下文中引发异常。

条件运算符

private static void DisplayFirstNumber(string[] args)
{
   string arg = args.Length >= 1 ? args[0] : 
                              throw new ArgumentException("You must supply an argument");
   if (Int64.TryParse(arg, out var number))
      Console.WriteLine($"You entered {number:F0}");
   else
      Console.WriteLine($"{arg} is not a number.");                            
  
}

null 合并运算符

public string Name
{
    get => name;
    set => name = value ?? 
        throw new ArgumentNullException("Name cannot be null", nameof(value));
}

expression-bodied lambda 或方法。

DateTime ToDateTime(IFormatProvider provider) => 
         throw new InvalidCastException("Conversion to a DateTime is not supported.");

参考:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/throw#the-throw-expression

9.Generalized async return types

10.数字语法改进(Numeric literal syntax improvements)

二进制

public const int One =  0b0001;
public const int Two =  0b0010;
public const int Four = 0b0100;
public const int Eight = 0b1000;

数字分隔符

public const long BillionsAndBillions = 100_000_000_000;
public const double AvogadroConstant = 6.022_140_857_747_474e23;
public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;

参考

https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/

相关文章

  • C# 6/7 新功能

    C#新功能 一、C#历史演变 C# 1,Visual Studio .NET 2002: C# 初版。 C# 1....

  • C#6中的新增功能 【Unity3D亲测】

    随着Unity2017的普及,使用.NET 4X的开发者也越来越多了,下面笔者给大家介绍一下在C# 6中的新功能主...

  • Lua 异常处理

    在写lua代码,特别是有热更新功能的项目中,c#中新加了接口之后,然后在lua中调用了此接口。但是项目中c#代码是...

  • 如何理解C#迭代器方法

    代器是C# 2.0中的新功能。它使类或结构支持foreach迭代,而不必“显示”实现IEnumerable或IEn...

  • 【第9篇】Kotlin的扩展

    1、扩展 Kotlin 同 C# 与 Gosu 类似,能够扩展一个类的新功能而无需继承该类或使用像装饰者这样的任何...

  • Kotlin - 扩展

    Kotlin 同 C# 和 Gosu 类似,能够扩展一个类的新功能而无需继承该类或使用像装饰者这样的任何类型的设计...

  • 学习JavaScript数据结构与算法(第2版).epub

    【下载地址】 本书首先介绍了JavaScript 语言的基础知识以及ES6 和ES7 中引入的新功能,接下来讨论了...

  • 真会 C# 吗 04

    声明 本文内容来自微软 MVP solenovex 的视频教程——真会C#吗?-- C#全面教程,大致和第 7 课...

  • 真会 C# 吗 03

    声明 本文内容来自微软 MVP solenovex 的视频教程——真会C#吗?-- C#全面教程,大致和第 6 课...

  • Javascript之ES7详解

    ES7+ES8 前言 本篇文章主要介绍ES7+ES8的一些新功能,并结合ES6的一些API做出了相应的比较。 ES...

网友评论

      本文标题:C# 6/7 新功能

      本文链接:https://www.haomeiwen.com/subject/mwpudxtx.html