C# 6/7 新功能

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/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容