精华内容
下载资源
问答
  • C# 特性

    2016-10-24 20:58:42
    C# 特性
  • C#新特性实例NewDemo.rar

    2020-03-11 11:51:09
    某教育机构C#新特性实例NewDemo
  • C#6.0新特性

    2016-09-03 13:51:13
    其中记录了C#6.0中的新特性
  • c#新特性

    2016-10-15 14:21:41
    c#新特性
  • C#6.0新特性Demo

    2016-07-10 22:52:23
    C#6.0新特性Demo
  • C# 8.0新特性介绍

    2020-08-28 23:49:31
    C# 语言是在2000发布的,至今已正式发布了7个版本,每个版本都包含了许多令人兴奋的新特性和功能更新。下面通过本文给大家分享下C# 8.0的三个令人兴奋的新特性,需要的朋友参考下吧
  • C#2.0新特性

    2007-05-15 07:42:18
    C#2.0新特性C#2.0新特性
  • C# 6.0 新特性汇总

    2020-09-01 13:05:23
    主要介绍了C# 6.0 新特性汇总的相关资料,本文给大家带来了11种特征,非常不错,感兴趣的朋友一起看看吧
  • C#新特性详解实用chm格式,实用详细,明了。
  • c#语言新特性

    2015-04-28 20:45:16
    c#语言分析特性,比较有用,包括泛型,var类型,部分类和部分方法等
  • c# 4.0新特性详解

    2009-12-17 08:41:29
    c# 4.0新特性,vs2010的新特性,里面详细解释了特性的使用。
  • C#9.0 新特性简介

    2021-01-19 23:37:15
    前写天收到了活跃在C#一线的BASSAM ALUGILI给我分享C# 9.0新特性,我在他文章的基础上进行翻译,希望能对大家有所帮助. 这是世界上第一篇关于C#9候选功能的文章。阅读完本文后,你将会为未来可能遇到的C# 9.0新特性...
  • C#语言新特性

    2016-02-26 11:28:54
    基础编程语言,打下扎实基础,轻松解决编程难题!! C#教程
  • C#2新特性

    2008-01-31 20:40:21
    C#2新特性.pdf
  • c# 4.0新特性一览

    2010-07-30 09:24:33
    c# 4.0新特性一览PPT c# 4.0新特性一览PPT
  • C#4.0新特性 C#4.0中文帮助文档 C#4.0
  • C# 自定义特性

    2013-07-13 16:24:36
    C# 特性 Attribute
  • C#7.0的新特性

    2019-04-07 17:46:15
    C#7.0的新特性
  • C# 8.0 新特性

    千次阅读 2018-11-13 17:20:23
    C# 8.0 新特性"]    C# 的下一个主要版本是 8.0。我们已经为它工作了很长一段时间,即使我们构建并发布了次要版本 C# 7.1, 7.2 和 7.3,我仍然对 8.0 将带来的新特性感到非常兴奋。    目前的计划是 C# 8.0...

      [译注:原文主标题如此,但内容大部分为新特性介绍,所以意译标题为 "C# 8.0 新特性"]
      
      C# 的下一个主要版本是 8.0。我们已经为它工作了很长一段时间,即使我们构建并发布了次要版本 C# 7.1, 7.2 和 7.3,我仍然对 8.0 将带来的新特性感到非常兴奋。
      
      目前的计划是 C# 8.0 将与 .NET Core 3.0 同时发布。然而,随着我们正在开发的 Visual Studio 2019 的预览版,这些特性将开始活跃起来。当这些出来的时候,您就可以开始尝试它们,我们将提供有关各个特性的更多细节。这篇文章的目的是向您简述预期的内容,以及如何理解它们。
      
      C# 8.0 新特性
      
      下面是 C# 8.0 中最重要的新特性的概述。还有一些较小的改进正在进行中,这些改进将在未来几个月逐渐显现出来。
      
      Nullable reference types 可空引用类型
      
      此特性的目的是帮助处理无处不在的空引用异常,这种异常已经困扰了半个世纪的面向对象编程。
      
      这个特性阻止您将 null 放入普通引用类型中(如字符串),从而使这些类型不可为 null!不过它是温和的提示警告,而不是错误。所以,它会让现有代码出现新的警告,因此您必须有选择的使用该功能 (您可以在项目、文件甚至行级别执行此操作)。
      
      string s = null; // Warning: Assignment of null to non-nullable reference type
      
      如果您确实想要 null 怎么办?可以使用一个可空引用类型,例如 string? 这样:
      
      string? s = null; // Ok
      
      当您尝试使用可空引用类型时,你首先需要检查是否为空。编译器会分析代码流,以查看 null 值是否可以将其用于当前位置:
      
      void M(string? s)
      
      {
      
      Console.WriteLine(s.Length); // Warning: Possible null reference exception
      
      if (s != null)
      
      {
      
      Console.WriteLine(s.Length); // Ok: You won't get here if s is null
      
      }
      
      }
      
      这个特性的要点是,C# 允许您表达“可空的意图”,并且在您不遵守它时候发出警告。
      
      Async streams 异步流
      
      C# 5.0 的 async/await 特性使您可以用非常简单的代码消费(或生产)异步结果, 而无需回调:
      
      async Task<int> GetBigResultAsync()
      
      {
      
      var result = await GetResultAsync();
      
      if (result > 20) return result;
      
      else return -1;
      
      }
      
      如果您想要消费(或生产)连续的结果流(例如您可能从物联网设备或云服务获得),则没有那么有用。 异步流就是为此而存在的。
      
      如果您想要消费(或生产)连续的结果流(例如您可能从物联网设备或云服务获得),则没有那么有用。 异步流就是为此而存在的。
      
      我们现在介绍一下您所期望的 IAsyncEnumerable<T>,即 IEnumerable<T> 的异步版本。允许您 await foreach 以消费它们的元素,并 yield return 以生产元素。
      
      async IAsyncEnumerable<int> GetBigResultsAsync()
      
      {
      
      await foreach (var result in GetResultsAsync())
      
      {
      
      if (result > 20) yield return result;
      
      }
      
      }
      
      Ranges and indices 范围和索引
      
      我们正在添加一个类型 Index,可用于索引。您可以创建一个整型来表示从头开始的索引,或者一个 ^ 前缀的从结尾表示的索引:
      
      Index i1 = 3;  // number 3 from beginning
      
      Index i2 = ^4; // number 4 from end
      
      int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
      
      Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"
      
      我们还引入了一个 Range 类型,它由两个 Index 组成,一个用于开始,一个用于结束,并且可以用 x..y 这样的范围表达式来编写。然后,您可以使用 Range 进行索引来生成切片:
      
      var slice = a[i1..i2]; // { 3, 4, 5 }
      
      Default implementations of interface members 接口成员的默认实现
      
      现在,一旦你发布了一个接口,游戏就结束了:你不能在不破坏它的所有现有实现的情况下向它添加成员。
      
      在 C# 8.0 中,我们允许您为接口成员提供一个默认实现。因此,如果某人没有实现该成员(可能因为他们编写代码时还没有该成员),他们将只得到默认的实现。
      
      interface ILogger
      
      {
      
      void Log(LogLevel level, string message);
      
      void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload
      
      }
      
      class ConsoleLogger : ILogger
      
      {
      
      public void Log(LogLevel www.huarenyl.cn level, string message) { ... }
      
      // Log(Exception) gets default www.hjshidpt.com implementation
      
      }
      
      ConsoleLogger 类不必实现 ILogger 中 Log(www.dfgjpt.com Exception) 重载函数,因为它已经定义了默认实现。现在只要提供了一个默认实现,您就可以添加新的成员到已经存在的公开接口中了。
      
      Recursive patterns 递归的模式匹配
      
      在模式匹配中,现在允许模式中包含其他模式。
      
      IEnumerable<string> GetEnrollees()
      
      {
      
      foreach (var p in People)
      
      {
      
      if (p is Student { Graduated: false, Name: string name }) yield return name;
      
      }
      
      }
      
      这个模式 Student { Graduated: false, Name: string name } 会检查 Person 是否是 Student,然后将常量模式 false 应用于 Graduated 属性以查看它们是否已毕业,并将模式字符串 name 添加到其 Name 属性中,得到他们的名字(如果非空)。因此,如果 p 是 Student,没有毕业并且具有非空的名字,则返回该名字。
      
      Switch expressions Switch 表达式
      
      带有模式的 switch 语句在 C# 7.0 中非常强大,但编写起来很麻烦。switch 表达式是一个“轻量级”版本,其中所有情况都是表达式:
      
      var area = figure switch
      
      {
      
      Line _      => 0,
      
      Rectangle r =www.furong157.com> r.Width * r.Height,
      
      Circle c    =www.michenggw.com/> c.Radius * 2.0 * Math.PI,
      
      _           =www.mengzhidu178.com> throw new UnknownFigureException(figure)
      
      };
      
      Target-typed new-expressions 已知目标类型的新表达式
      
      在许多情况下,当您创建新对象时,类型已经可以从上下文中知道。在这些情况下,可以省略类型:
      
      Point[] ps = { new (1, 4), new (3,-2), new (9, 5) }; // all Points
      
      该功能的实现由社区成员提供,谢谢!
      
      平台依赖性
      
      大多数 C# 8.0 语言特性都可以在任何版本的 .NET 上运行。但是,其中一些具有平台依赖性。
      
      Async streams, Index 和 Range 都依赖于 .NET Standard 2.1 的新类型。正如 Immo 在他的文章《公布.NET Standard 2.1》所说的那样,.NET Core 3.0 、Xamarin 、Unity 和 Mono 都将实现 .NET Standard 2.1,但 .NET Framework 4.8 不会。这意味着当您将 C# 8.0 指向到 .NET Framework 4.8 时,使用这些功能所需的类型将不可用。
      
      与往常一样,C# 编译器对它所依赖的类型非常宽容。如果它能找到具有正确的名字和形态的类型,则很乐意将它们作为目标。
      
      默认接口实现依赖于新的增强运行时,我们也不会在 .NET Runtime 4.8 中实现这些。因此,此特性不适用于 .NET Framework 4.8 和旧版本的 .NET。
      
      十余年间,为了保持运行时的稳定,我们无法在其中实现新的语言特性。随着现代化运行时的并行性和开源性,我们觉得可以负责任地去重新开发它们,并在考虑到这一点时进行语言设计。 Scott 在其 .NET Core 3.0 和 .NET Framework 4.8 更新中解释说,.NET Framework 将来会看到较少的创新,而是关注稳定性和可靠性。考虑到这一点,我们认为,直接忽略某些语言特性会好一些。
      
      想要了解更多?
      
      C# 语言的设计过程是开源的,在这个repo中。如果您不经常跟进,可能会有点混乱和力不从心。语言设计的核心是语言设计会议,记录在 C# 语言设计日记。
      
      大约一年前,我写了一篇介绍C#中的可空引用类型的文章。您仍然可以阅读它并得到一些信息。。
      
      您还可以观看视频,例如 Microsoft Build 2018 大会上的 C# 未来,或者 .NET Conf 2018 大会上的即将到来的 C#,它展示了其中一些特性。
      
      Kathleen 有一篇很好的帖子来阐述了 .Net Core 3.0 中的 Visual Basic 的计划。
      
      当我们开始将这些功能作为 Visual Studio 2019 预览版的一部分发布时,我们还将发布有关各个功能的更多详细信息。
      
      就个人而言,我迫不及待地要把它们交到你们所有人手中!

    展开全文
  • 探秘C# 6.0 的新特性

    2020-09-03 21:27:03
    本文的内容包括引入C#6.0中的的语言特性有哪些. 还有已经被引入的代码名称为 “Roslyn”编译器. 编译器是开放源码的,并且可以从 codeplex 网站的这个地址下载到源代码:https://roslyn.codeplex.com/.
  • C# 9.0新特性

    千次阅读 2019-06-14 16:29:42
    CandidateFeaturesForCSharp9 ...前写天收到了活跃在C#一线的BASSAM ALUGILI给我分享C# 9.0新特性,我在他文章的基础上进行翻译,希望能对大家有所帮助. 这是世界上第一篇关于C#9候选功能的文章。阅读完本文后...

    CandidateFeaturesForCSharp9

    看到标题,是不是认为我把标题写错了?是的,C# 8.0还未正式发布,在官网它的最新版本还是Preview 5,通往C#9的漫长道路却已经开始.前写天收到了活跃在C#一线的 BASSAM ALUGILI 给我分享C# 9.0新特性,我在他文章的基础上进行翻译,希望能对大家有所帮助.

    这是世界上第一篇关于C#9候选功能的文章。阅读完本文后,你将会为未来可能遇到的C# 9.0新特性做好更充分的准备。

    这篇文章基于,

    原生大小的数字类型

    这次引入一组新类型(nint,nuint,nfloat等)'n'表示 native(原生) ,该特性允许声明一个32位或64位的数据类型,这取决于操作系统的平台类型。

    nint nativeInt = 55; take 4 bytes when I compile in 32 Bit host.    
    nint nativeInt = 55; take 8 bytes when I compile in 64 Bit host with x64 compilation settings.

    xamarin中已存在类似的概念,

    Records and Pattern-based With-Expression

    这个功能我等待了很长时间,Records是一种轻量级的不可变类型,它可以是方法,属性,运算符等,它允许我们进行结构的比较, 此外,默认情况下,Records属性是只读的。

    Records可以是值类型或引用类型。

    Example

    public class Point3D(double X, double Y, double Z);    
    public class Demo     
    {    
      public void CreatePoint()    
      {    
      var p = new Point3D(1.0, 1.0, 1.0);  
      }  
    }

    这些代码会被编译器转化如下形式.

    public class Point3D    
    {    
    private readonly double <X>k__BackingField;    
    private readonly double <Y>k__BackingField;    
    private readonly double <Z>k__BackingField;    
    public double X {get {return <X>k__BackingField;}}    
    public double Y{get{return <Y>k__BackingField;}}    
    public double Z{get{return <Z>k__BackingField;}}    
        
     public Point3D(double X, double Y, double Z)    
     {    
     <X>k__BackingField = X;    
     <Y>k__BackingField = Y;    
     <Z>k__BackingField = Z;    
     }    
        
     public bool Equals(Point3D value)    
     {    
      return X == value.X && Y == value.Y && Z == value.Z;    
     }    
         
     public override bool Equals(object value)    
     {    
      Point3D value2;    
      return (value2 = (value as Point3D)) != null && Equals(value2);    
     }    
        
     public override int GetHashCode()    
     {    
      return ((1717635750 * -1521134295 +  EqualityComparer<double>.Default.GetHashCode(X)) * -1521134295 + EqualityComparer<double>.Default.GetHashCode(Y)) * -1521134295 +  EqualityComparer<double>.Default.GetHashCode(Z);    
     }    
    }    
         
    Using Records:    
         
    public class Demo    
    {    
     public void CreatePoint()    
     {    
     Point3D point3D = new Point3D(1.0, 1.0, 1.0);    
     }    
    }

    Records迎合了基于表达式形式编程的特性,使得我们可以这样使用它.

    var newPoint3D = Point3D.With(x: 42);

    这样我们创建的新Point(new Point3D)就像现有的一个(point3D)一样并把X的值更改为42。

    这个特性于基于 pattern matching 也非常有效,我会在我的下一篇文章中介绍这一点.

    那么我们为什么要使用Records而不是用结构体呢?为了回答这些问题,我引用了了Reddit的一句话:

    “结构体是你必须要有一些约定来实现的东西。你不必手动地去让它只读,你也不用去实现他们的比较逻辑,但如果你不这样做,那你就失去了使用结构体的意义,编译器不会强制执行这些约束"。

    Records类型由是编译器实现,这意味着您必须满足所有这些条件并且不能错误, 因此,它们不仅可以减少重复代码,还可以消除一大堆潜在的错误。

    此外,这个功能在F#中存在了十多年,其他语言如(Scala,Kotlin)也有类似的概念。

    F#

    type Greeter(name: string) = member this.SayHi() = printfn "Hi, %s" name

    Scala

    class Greeter(name: String)     
    {    
       def SayHi() = println("Hi, " + name)    
    }

    Kotlin

    class Greeter(val name: String)     
    {    
     fun sayhi()     
     {    
     println("Hi, ${name}");    
     }    
    }

    在没有Records之前,我们要实现类似的功能,C#代码要这么写

    C#

    public class Greeter
    {
     private readonly string _name;
     public Greeter(string name)
     {
     _name = name;
     }
     public void Greet()
     {
      Console.WriteLine($ "Hello, {_name}");
     }
    }

    有了Records之后,我们可以将C#代码大大地减少了,

    ublic class Greeter(name: string)     
    {    
     public void Greet()     
     {    
     Console.WriteLine($ "Hello, {_name}");    
     }    
    }

    Less code! = I love it!

    Type Classes

    此功能的灵感来自Haskell,它是我最喜欢的功能之一。正如我两年前在我文章中所说,C#将实现更多的函数式编(FP)程概念,Type Classes就是FP概念之一。在函数式编程中,Type Classes允许您在类型上添加一组操作,但不实现它。由于实现是在其他地方完成的,这是一种多态,它比面向对象编程语言中的class更灵活。

    Type Classes和C#接口具有相似的用途,但它们的工作方式有所不同,在某些情况下,由于处理固定类型而不是继承层次结构,因此Type Classes更易于使用。

    此这特性最初与“extending everything”功能一起引入,您可以将它们组合在一起,如Mads Torgersen给出的例子所示。

    我引用了官方提案中的一些结论:

    “一般来说,”shape“(shape是Type Classes的一个新的关键字)声明非常类似于接口声明,除了以下情况,

    • 它可以定义任何类型的成员(包括静态成员)
    • 可以通过扩展实现
    • 只能在指定的地方当作一种类型使用(作用域)“

    Haskell中 Type Classes示例。

    class Eq a where     
    (==) :: a -> a -> Bool    
    (/=) :: a -> a -> Bool

    “Eq”是类名,而==,/ =是类中的操作。类型“a”是类“Eq”的实例。

    如果我们将上述例子用C#接口实现将会是这样.

    interface Num<A>     
    {    
     A Add(A a, A b);    
     A Mult(A a, A b);    
     A Neg(A a);    
    }    
         
    struct NumInt : Num<int>     
    {    
     public int Add(int a, int b) => a + b;     
     public int Mult(int a, int b) => a * b;     
     public int Neg(int a) => -a;    
    }

    如果我们用Type Classes实现C# 功能会是这样

    shape Num<A>    
    {    
     A Add(A a, A b);    
     A Mult(A a, A b);    
     A Neg(A a);    
    }    
         
    instance NumInt : Num<int>    
    {    
     int Add(int a, int b) => a + b;     
     int Mult(int a, int b) => a * b;     
     int Neg(int a) => -a;    
    }

    通过上面例子,可以看到接口和shape的语法类似 ,那我们再来看看Mads Torgersen给出的例子

    Note:shape不是一种类型。相反,shape的主要目的是用作通用约束,限制类型参数以具有正确的形状,同时允许通用声明的主体使用该形状,

    原始来源

    public shape SGroup<T>      
    {      
     static T operator +(T t1, T t2);      
     static T Zero {get;}       
    }

    这个声明说如果一个类型在T上实现了一个+运算符并且具有0静态属性,那么它可以是一个SGroup 。

    给int添加静态成员Zero

    public extension IntGroup of int: SGroup<int>    
    {    
     public static int Zero => 0;    
    }

    定义一个AddAll方法

    public static AddAll<T>(T[] ts) where T: SGroup<T> // shape used as constraint    
    {    
     var result = T.Zero; // Making use of the shape's Zero property    
     foreach (var t in ts) { result += t; } // Making use of the shape's + operator    
     return result;    
    }

    让我们用一些整数调用AddAll方法,

    int[] numbers = { 5, 1, 9, 2, 3, 10, 8, 4, 7, 6 };        
    WriteLine(AddAll(numbers)); // infers T = int

    这就是Type class 的妙处,慢慢消化感受一下??

    Dictionary Literals

    引入更简单的语法来创建初始化的Dictionary <TKey,TValue>对象,而无需指定Dictionary类型名称或类型参数。使用用于数组类型推断的现有规则推断字典的类型参数。

    // C# 1..8    
    var x = new Dictionary <string,int> () { { "foo", 4 }, { "bar", 5 }};   
    // C# 9    
    var x = ["foo":4, "bar": 5];

    该特性使C#中的字典工作更简单,并删除冗余代码。此外,值得一提的是,在F#和Swift等其他编程语言中也使用了类似的字典语法。

    Params Span

    允许params语法使用Span 这个帮助来实现没有任何堆分配的params参数传递。此功能可以使params方法的使用更加高效。

    新的语法如下,

    void Foo(params Span<int> values);

    struct允许使用无参构造函数

    到目前为止,在C#中不允许在结构体声明中使用无参构造函数,在C#9中,将删除此限制。

    StackOverflow示例

    public struct Rational    
    {    
     private long numerator;    
     private long denominator;    
        
     public Rational(long num, long denom)    
     { /* Todo: Find GCD etc. */ }    
        
     public Rational(long num)    
     {    
      numerator = num;    
      denominator = 1;    
     }    
         
     public Rational() // This is not allowed    
     {    
      numerator = 0;    
      denominator = 1;    
     }    
    }

    链接到StackOverflow示例

    其实CLR已经允许值类型数据具有无参构造函数,只是C# 对这个功能进行了限制,在C# 9.0中可能会消除这种限制.

    固定大小的缓冲区

    这些提供了一种通用且安全的机制,用于向C#语言声明固定大小的缓冲区。

    目前,用户可以在不安全的环境中创建固定大小的缓冲区。但是,这需要用户处理指针,手动执行边界检查,并且只支持一组有限的类型(bool,byte,char,short,int,long,sbyte,ushort,uint,ulong,float和double)。该特性引入后将使固定大小的缓冲区变得安全安全,如下例所示。

    可以通过以下方式声明一个安全的固定大小的缓冲区,

    public fixed DXGI_RGB GammaCurve[1025];

    该声明将由编译器转换为内部表示,类似于以下内容,

    [FixedBuffer(typeof(DXGI_RGB), 1024)]    
    public ConsoleApp1.<Buffer>e__FixedBuffer_1024<DXGI_RGB> GammaCurve;    
        
    // Pack = 0 is the default packing and should result in indexable layout.    
    [CompilerGenerated, UnsafeValueType, StructLayout(LayoutKind.Sequential, Pack = 0)]    
    struct <Buffer>e__FixedBuffer_1024<T>    
    {    
     private T _e0;    
     private T _e1;    
     // _e2 ... _e1023    
     private T _e1024;    
     public ref T this[int index] => ref (uint)index <= 1024u ?    
     ref RefAdd<T>(ref _e0, index):    
     throw new IndexOutOfRange();    
    }

    Uft8字符串文字

    它是关于定义一种新的字符串类型UTF8String,它将是,

    System.UTF8String myUTF8string ="Test String";

    Base(T)

    此功能用于解决默认接口方法中的 覆盖冲突问题 :

    interface I1
    { 
        void M(int) { }
    }
    
    interface I2
    {
        void M(short) { }
    }
    
    interface I3
    {
        override void I1.M(int) { }
    }
    
    interface I4 : I3
    {
        void M2()
        {
            base(I3).M(0) // Which M should be used here? What does this do?
        }
    }

     

    摘要

    您已经阅读了第一个C#9候选特性。正如您所看到的,许多新功能受到其他编程语言或编程范例的启发,而不是自我创新,这些特性大部分在在社区中得到了广泛认可,所以引入C# 后应该也会给大家带来不错的体验.

    展开全文
  • C# 6.0新特性整理

    千次阅读 2016-12-15 18:10:29
    C# 6.0中引入的新特性,以下是测试过的 1.自动的属性初始化器 Auto Property Initializer /// /// C#6.0 自动属性不需要编写构造器就能添加初始值 /// public class AutoPropertyInCsharp6 { public int PostID {...

    C# 6.0中引入的新特性,以下是测试过的

    1.自动的属性初始化器 Auto Property Initializer

    /// <summary>
    /// C#6.0 自动属性不需要编写构造器就能添加初始值
    /// </summary>
    public class AutoPropertyInCsharp6
    {
        public int PostID { get; } = 1;
        public string PostName { get; } = "Post1";
        public string PostTitle { get; protected set; } = string.Empty;
    
        public AutoPropertyInCsharp6()
        {
            //实验证明,在构造器中PostID已经有了初始值
            Console.WriteLine(PostID);
        }
    }

    1.1 为访问器添加访问限制

    public string FirstName { get; private set; }
    public string LastName { get; private set; }
    2. 字典初始化器 Dictionary Initializer

    /// <summary>
    /// 字典类型数据初始化
    /// </summary>
    public class DictionaryInitializerInCSharp6
    {
        /// <summary>
        /// 之前的字典初始化器
        /// </summary>
        public Dictionary<string, string> List1 = new Dictionary<string, string>() {
            { "name1","12"},
            { "name2","13"},
        };
        /// <summary>
        /// C#6.0 增加的字典初始化器
        /// </summary>
        public Dictionary<string, string> List2 = new Dictionary<string, string>()
        {
            ["name1"] = "12",
            ["name2"] = "13",
        };
    }


    3. 静态的Using Static Using

    //C#6.0 引用静态类的方式
    using static System.Console;
    
    namespace Grammar2._1
    {
        public class StaticUsingInCSharp6
        {
            public void Test1()
            {
                WriteLine("Static Using in C# 6");
            }
        }
    } 


    4.catch 块中的 await

    /// <summary>
    /// 声明一个异步的方法
    /// </summary>
    public async void CatchTest()
    {
        try
        {
            int a = Convert.ToInt32("a");
            Console.WriteLine(a);
        }
        catch (Exception ex)
        {
            await doSomething(ex);
        }
    }
    private Task doSomething(Exception ex)
    {
        return Task.Run(() =>
        {
            Console.WriteLine("doSomething:" + ex.Message);
        });
    }

    5.异常过滤器 Exception Filter 

    /// <summary>
    /// try/catch 语句when关键词过滤
    /// </summary>
    public void CacheTest2()
    {
        try
        {
            int a = Convert.ToInt32("a");
            Console.WriteLine(a);
        }
        catch (Exception ex) when (ex.InnerException == null)
        {
            Console.WriteLine("ex.InnerException为空");
        }
    }

    6.用于检查NULL值的条件访问操作符

    /// <summary>
    /// 用于检查null值得条件访问操作符 ?.
    /// </summary>
    public void NullTest1()
    {
        StaticUsingInCSharp6 auto1 = new StaticUsingInCSharp6();
        // StaticUsingInCSharp6 auto1 = null;
        string str = null;
        str = auto1?.Str ?? "空字符串";
        Console.WriteLine(str);
    }
    public string Str { get; set; } = "abc";


    7.字符串插值处理

    /// <summary>
    /// C# 6.0 字符串插值处理
    /// </summary>
    public class StringInterpolation
    {
        public static string Test1()
        {
            string first = "san";
            string send = "zhang";
            string result = $"{send}--{first}";
            Console.WriteLine(result);
            return result;
        }
        public static string FirstName { get; set; } = "san";
        public static string SendName { get; set; } = "zhang";
        public static string FullName  => $"{FirstName}--{SendName}";
        public static string GetAll() => $"Name:{FullName},Name2:{StringInterpolation.Test1()}";
    }



    MSDN说明文档:https://docs.microsoft.com/zh-cn/dotnet/articles/csharp/csharp-6

    参考文件:http://www.oschina.net/translate/new-features-in-csharp

    展开全文
  • c# 自定义特性demo

    2016-06-29 22:01:50
    C# 自定义特性 例子
  • c#4.0新特性

    2012-08-07 11:20:13
    Microsoft Visual C# 3.0作为Visual Studio 2008...在VS Managed Languages团队,我们一直努力创建该语言的下一个版本(没什么惊喜,就是C# 4.0),而这个文档是我们现在所看到的、计划中的语言特性的第一个公开描述。
  • C#4.0的一些新特性

    2011-04-02 11:52:32
    C#4.0的一些新特性 C#4.0的一些新特性
  • C#6.0,C#7.0新特性

    千次阅读 2018-06-06 13:49:34
    C#6.0新特性 Auto-Property enhancements(自动属性增强) Read-only auto-properties (真正的只读属性) Auto-Property Initializers (自动属性的初始化) Expression-bodied function members (表达式方法体)...

    C#6.0新特性


    Auto-Property enhancements(自动属性增强)

    Read-only auto-properties (真正的只读属性)

    // 以前
    // 只是限制了属性在类外部只读,而在类内部任何地方都可设置
    public string Name { get; private set; }
    public void SetName(string name) {
        Name = name;
    }
    
    // c#6.0
    // 1.通过只使用一个getter来声明真正只读
    // 2.这样的属性,只能在构造器中初始化(含属性声明时),而类内部其他地方也不可再设置
    public string Name { get; } = "小米喂大象"; // 允许
    public User(string Name, string password, int age) {
        Name = name; // 允许
        Password = password;
        Age = age;
    }
    public void SetName(string name) {
        Name = name;  // 报错
    }

    Auto-Property Initializers (自动属性的初始化)

    // 以前
    // 需要属性有setter,通过setter来初始化backing field
    public string Name { get; set; }
    public User(string name) {
        Name = "小米喂大象";
    }
    
    // C#6.0
    // 可以在属性声明的同时初始化
    public string Name { get; } = "小米喂大象";
    public int Age { get; set; } = 18;

    Expression-bodied function members (表达式方法体)

    // C#6.0
    // 1. 类成员方法体是一句话的,都可以改成使用表达式,使用Lambda箭头来表达
    // 2. 只适用于只读属性和方法
    public string Name => "小米喂大象"; // 只读属性
    public void SetAge(int age) => Age = age; // 方法
    public void Log(string msg) => System.Console.WriteLine($"{Name} : {msg}");

    using static (导入类静态方法)

    // c#6.0
    // 1.使用using static 语法,可以将一个类中的所有静态方法导入到当前上下文,
    //   包括这个类中的嵌套类型,不包括实例方法,也不包括const字段
    // 2.这样引用这个类的方法,就可以直接引用,而不用再加前缀(形似C函数)。
    using static System.Math;
    using static System.String;
    
    public Double Calc(int angle) {
        var tmp = Sin(angle) + Cos(ange);
        ...
    }

    Null-conditional operators (一元空值检查操作符?.)

    // 以前
    // 一个引用的null检查和使用是分开的
    User user;
    . . .
    if (user != null) {
        user.SetAge(19);
    }
    
    // C#6.0
    // 1. 直接使用?.代替.操作符即可
    // 2. ?.操作符确保其左边表达式只计算一次
    // 2. 如果引用是null,这直接返回类型匹配的null, 下面的name被推断为string?
    user?.SetAge(19);
    var name = user?.Name;

    String Interpolation (字符串插值)

    // 以前
    public override string ToString() {
        return string.Format("{0}:{1:D2}", Name, Age);
    }
    
    // C#6.0
    // 1. 使用$开头,花括号里直接放入表达式
    // 2. 格式化字符串,可直接在花括号里表达式后面加上:,然后加上格式化字符串
    // 3. 插值表达式里可以嵌套插值表达式
    public override string ToString() {
        return $"{Name}:{Age:D2}";
    }

    nameof Expressions (nameof 表达式)

    // C#6.0
    // 1. nameof表达式返回一个变量、属性或字段的名称
    // 2. 当需要一个符号的名称时很有用,一可以避免手工打错,二可以便于重构
    // 3. 如果是一个限定了前缀的完整名称,如nameof(User.Age),
    //    nameof操作符也只是返回"Age",而不是"User.Age"
    public string Name {
        get => name;
        set {
            name = value;
            PropertyChanged?.Invoke(this, 
                new PropertyChangedEventArgs(nameof(Name)));
        }
    }
    
    public int Age {
        get => age;
        set {
            age = value;
            PropertyChanged?.Invoke(this, 
                new PropertyChangedEventArgs(nameof(User.Age)));
        }
    }

    Index Initializers(索引初始化器)

    // c#6.0
    // 允许使用[]操作符来初始化,这样字典可以像其他序列容器一样的语法初始化了
    public Dictionary<string, User> Users = new Dictionary<string, User> {
        ['小米喂大象'] = new User(),
        ['Jack'] = new User(),
    }

    Exception Filters (异常过滤器)

    // C#6.0
    // 1. catch字句后面可以带一个 when表达式(早期是if,后被when替换)
    // 2. 如果when括号内表达式(下面代码?部分)为真则Catch块就执行,否则不执行
    // 3. 利用这个表达式可以做很多事,包括过滤指定异常、调试、打印日志等。
    try {
    } catch (Excepation e) when (?) {
    }

    Await in Catch and Finally blocks (Catch,Finally语句块中可用await)

    // C#6.0
    // 1. C#5.0中添加了async、await, 但是在哪里放await表达式,这个有一些限制
    // 2. C#6.0中解决了这些限制中的其中一个,就是await可以放在catch、finally语句块中了。
    try {
        var result = await SomeTask;
        return result;
    } catch (Exception e) {
        await Log(e);
    } finally {
        await Cleanup();
    }

    Extension Add methods in collection initializers (在集合初始化器中使用扩展的Add方法)

    // c#6.0
    public class User {
        public string Name { get; set; }
        public int Age { get; set; }
        public User(string name, int age) {
            Name = name;
            Age = age;
        }
    }
    
    public class ActiveUsers : IEnumerable<User> {
        List<User> users = new List<User>();     
    
        //public void Add(User user) {
        //    users.Add(user);
        //}
    
        public void Append(User user) {
            users.Add(user);
        }
    
        public IEnumerator<User> GetEnumerator() {
            throw new NotImplementedException();
        }
    
        IEnumerator IEnumerable.GetEnumerator() {
            throw new NotImplementedException();
        }
    }
    
    // 添加一个扩展的静态方法Add
    public static class ActiveUsersExtensions {
        public static void Add(this ActiveUsers users, User u) 
            => users.Append(u);
    }
    
    public class Test {
        // 1. 为了能够像下面这样使用集合初始化器,ActiveUsers必须拥有一个Add()方法
        // 2. 现在,如果ActiveUsers 类只有一个Append()方法,没有Add()方法,而你也
        //    无法修改这个类,这时候下面的代码就无法工作了。
        // 3. C#6.0中允许你添加一个扩展的静态方法Add来完成工作。
        ActiveUsers users = new ActiveUsers {
            new User("xm01", 18),
            new User("xm02", 19),
            new User("xm03", 20),
        };
    }

    Improved overload resolution (改进的重载解析)

    // C#6.0
    // 1. 下面代码,C#6.0以前,当编译器看到Foo(Bar),会去匹配一个最合适的方法,但是,
    //    编译器最终会报告失败。因为编译器在匹配函数签名的时候,并没有将函数返回值作为
    //    一部分,所以编译器不能明确该调用哪个Foo方法。
    // 2. 同样,C#6.0以前,编译器不能区分Task.Run(Action)和Task.Run(Func<Task>())
    // 3. C#6.0解决了此问题 
    int Bar() { return 1; }
    void Foo(Action f) { }
    void Foo(Func<int> f) { }
    void Main() {
       Foo(Bar);
    }

    C#7.0新特性


    out variables (out 变量)

    // 以前
    // out变量的声明和初始化是分开的
    int age;
    if (int.TryParse("18", out age)) {
        Console.WriteLine("age: " + age);
    }
    
    // C#7.0
    // 1. C#7.0允许直接在方法的调用列表中声明一个out变量
    // 2. 这个变量可以声明成var这种隐式类型
    // 3. 这个变量的作用域范围伸展到if语句块的外部范围
    if (int.TryParse("18", out int age)) {
        Console.WriteLine("age: " + age);
    }
    if (int.TryParse("90", out var score)) { // 隐式类型也可以用
        Console.WriteLine("score: " + score);
    }
    age += 1; // 这时候age仍然有效

    Tuples (元组)

    // c#7.0
    // 1. C#7.0以前就已经有tuple, 但不是语言层面支持的,而且使用起来没效率
    // 2. C#7.0中使用tuple,需要引入 System.ValueTuple(如果平台不包含的话)
    // 3. 元组成员名可指定,不指定默认Item1,Item2,...
    // 4. 元组是值类型,其元素是公开字段,可修改
    // 5. 元组中元素都相等,则元组相等
    // 6. 元组可用于函数返回多个独立变量,这样不用定义一个struct或class
    // 7. 元组使用场合:
    
    // 元组成员名默认Item1, Item2
    var name = ("Jack", "Ma"); 
    (string, string) name1 = ("Jack", "Ma");
    
    // 指定成员名称为firstName, lastName
    (string firstName, string lastName) name2 = ("Jack", "Ma"); 
    
    // 指定成员名称为f, l
    var name3 = (f: "Jack", l: "Ma"); 
    
    // 左右同时指定成员名称,右边的忽略
    (string firstName, string lastName) name4 = (f: "Jack", l: "Ma"); 
    
    // 返回一个元组
    private (string FirstName, string LastName) GetName() {
        return ("Jack", "Ma");
    }
    var name5 = GetName();
    name5.FirstName = "Jack2";
    name5.LastName = "Ma2";
    
    // 析构元组成员到变量firstName, lastName
    (string firstName, string lastName) = GetName(); 
    firstName = "Jack2";
    lastName = "Ma2";
    
    // 析构元组成员到变量f, l
    (string f, string l) = name5;
    f = "Jack2";
    l = "Ma2";
    
    // 析构元组成员到变量f2, l2
    var (f2, l2) = name5;

    Discards (占位符)

    // C#7.0
    // 1. 增加一个占位符_(下划线字符)来表示一个只写的变量,这个变量只能写,不能读。
    //    当想丢弃一个值的时候,可以使用。
    // 2. 他不是实际变量,没有实际存储空间,所以可以多处使用。
    // 3. 一般用于解构元组、调用带out参数的方法、模式匹配,例如:
    //    > 调用一个方法,这个方法带有一个out参数,你根本不使用也不关心这个参数;
    //    > 一个包含多个字段的元组,你只关心其中部分成员,不关心的成员可以使用占位符;
    //    > 模式匹配中, _可以匹配任意表达式;
    // 4. 注意:_也是一个有效的变量标识符,在合理的情景下,_也会作为一个有效变量
    
    private (string FirstName, string LastName) GetName() {
        return ("Jack", "Ma");
    }
    
    private void GetName(out string FirstName, out string LastName) {
        FirstName = "Jack";
        LastName = "Ma";
    }
    
    // 只关心FirstName, LastName丢弃
    var(firstName, _) = GetName();
    GetName(out var firstName2, out _);
    
    // 有效变量_
    public void Work(int _) {
       _ += 4;
    }

    Pattern matching (模式匹配)

    // C#7.0
    // 模式匹配:匹配一个值是否具有某种特征(例如:是否是某个常量、某个类型、某个变量),
    //          如果是,顺便可将这个值提取到对应特征的新变量中
    // C#7.0中,利用已有的关键字is和switch来扩展,实现模式匹配
    
    // 具有模式匹配的is表达式:不仅能匹配类型,还能匹配表达式
    public static void TestIs(object o) {
        const string IP = "127.0.0.1";
    
        // 匹配常量
        if (o is IP) {
            Console.WriteLine("o is IP");
        }
    
        if (o is null) {
            Console.WriteLine("o is null"); 
        }
    
        // 匹配类型
        if (o is float) {
            Console.WriteLine($"o is float");
        } 
    
        // 匹配类型,并提取值。检测为true,这时候i会被明确赋值
        if (o is int i) {
            Console.WriteLine($"o is int {i}");
        } else {
            return;
        }
    
        // i仍然有效
        // i变量称为模式变量,和out变量一样,统称为表达式变量,作用域都扩展到了外围
        // 表达式变量的范围扩展到了外围,只有在前面的模式匹配为true是才有效
        // 表达式变量为true时,才给变量明确赋值,这样避免了模式不匹配时访问这些变量
        i++;
        Console.WriteLine($"i is {i}");    
    
        if (o is 4 * 4) {
            Console.WriteLine("o is 4*4");
        }
    }
    
    // 可以模式匹配的switch
    // 1. 原来的switch限制为仅仅是string和数字类型的常量匹配,现在解除了
    // 2. switch按照文本顺序匹配,所以需要注意顺序;
    //   (原来switch的分支只匹配一个所以不需要顺序;而现在可以匹配多个,行为变了)
    // 3. case子句后面可以带模式匹配的表达式
    // 4. default最后执行,也就是其他都不匹配时才执行,不管default语句放在什么位置。
    // 5. 如没有default分支,其他也不匹配,则不执行任何switch块代码,直接执行其后面代码
    // 6. case后带var形式变量的匹配,近似于default
    // 7. case 子句引入的模式变量只在switch块内有效
    public static void TestSwitch(object o) {
        switch (o) {
            case "127.0.0.1":
                Console.WriteLine("o is IP");
                break;
            case float f:
                Console.WriteLine($"o is float {f}");
                break;
            case int i when i == 4:
                Console.WriteLine($"o is int {i} == 4");
                break;
            case int i:
                Console.WriteLine($"o is int {i}");
                break;
            case string s when s.Contains("127"):
                Console.WriteLine("o is string, contains 127 ");
                break;
            case string s when s.Contains("abc"):
                Console.WriteLine("o is string, contains 127 ");
                break;
            case var a when a.ToString().Length == 0:
                Console.WriteLine($"{a} : a.ToString().Length == 0");
                break;
            case null:
                Console.WriteLine($"o is null");
                break;
            default:
                Console.WriteLine("default");
                break;
        }
    }

    Ref locals and returns (ref局部变量和返回ref变量)

    // C#7.0
    // C#7.0以前的ref只能用于函数参数,现在可以用于本地引用和作为引用返回
    // 1. 需要添加关键字ref,定义引用时需要,返回引用时也需要
    // 2. 引用声明和初始化必须在一起,不能拆分
    // 3. 引用一旦声明,就不可修改,不可重新再定向
    // 4. 函数无法返回超越其作用域的引用
    
    // 需要添加关键字ref,表示函数返回一个ref int
    public static ref int GetLast(int[] a) {
        if (a == null || a.Length < 1) {
            throw new Exception("");
        }
    
        int number = 18;
    
        // 错误声明: 引用申明和初始化分开是错误的 
        //ref int n1; 
        //n1 = number;
    
        // 正确声明: 申明引用时必须初始化,声明和初始化在一起
        // 添加关键字ref表示n1是一个引用, 
        ref int n1 = ref number;
    
        // n1指向number,不论修改n1或number,对双方都有影响,相当于双方绑定了。
        n1 = 19; 
        Console.WriteLine($"n1:{n1},  number:{number}");
        number = 20;
        Console.WriteLine($"n1:{n1},  number:{number}");
    
        // 语法错误,引用不可被重定向到另一个引用
        //n1 = ref a[2];
    
        // 语法正确,但本质是将a[2]的值赋值给n1引用所指,n1仍指向number
        n1 = a[2];
        Console.WriteLine($"n1:{n1},  number:{number}, a[2]:{a[2]}");
        number = 21;
        Console.WriteLine($"n1:{n1},  number:{number}, a[2]:{a[2]}");
    
    
        // --------------------- 引用返回 ------------------------ 
    
        // 错误:n1引用number,但number生存期限于方法内,故不可返回
        // return ref n1;
    
        // 正确:n2引用a[2],a[2]生存期不仅仅限于方法内,所以可以返回。
        ref int n2 = ref a[a.Length-1];
        return ref n2; // 需要ref返回一个引用
        return ref a[a.Length-1];  // 也可以直接返回一个引用
    }
    
    public static void Main(string[] args) {
        int[] a =  { 0, 1, 2, 3, 4, 5};
    
        // x不是一个引用,函数将值赋值给左侧变量x
        int x = GetLast(a);
        Console.WriteLine($"x:{x}, a[2]:{a[a.Length-1]}");
        x = 99;
        Console.WriteLine($"x:{x}, a[2]:{a[a.Length-1]} \n");
    
        // 返回引用,需要使用ref关键字,y是一个引用,指向a[a.Lenght-1]
        ref int y = ref GetLast(a);
        Console.WriteLine($"y:{y}, a[2]:{a[a.Length-1]}");
        y = 100;
        Console.WriteLine($"y:{y}, a[2]:{a[a.Length-1]}");
    
        Console.ReadKey();
    }

    Local functions (本地方法)

    // C#7.0
    // 1. 定义在一个方法体内的函数,称为本地方法
    // 2. 本地方法只在其外部方法体内有效
    // 3. 本地方法可定义在其外部方法的任何地方
    // 4. 外部方法其作用域内参数或变量,都可用于本地方法
    // 5. 本地方法实质被编译为当前类的一个私有成员方法,
    //    但被语言层级限制为只能在其外部方法内使用
    // 6. 由于(5),所以本地方法和类方法一样,没有特殊限制,异步、泛型、Lambda等都可用
    // 7. 常见用例:给迭代器方法和异步方法提供参数检查,因为这两类方法报告错误比较晚
    public static IEnumerable<int> SubsetOfIntArray(int start, int end) {
        if (start < end) {
            throw new ArgumentException($"start({start}) < end({end})");
        }
        return Subset();
    
        IEnumerable<int> Subset() {
            for (var i = start; i < end; i++)
                yield return i;
        }
    }

    More expression-bodied members(更多的 表达式方法体 成员)

    // C#7.0
    // 1. 类成员方法体是一句话的,都可以改成使用表达式,使用Lambda箭头来表达
    // 2. C#6.0中,这种写法只适用于只读属性和方法
    // 3. C#7.0中,这种写法可以用于更多类成员,包括构造函数、析构函数、属性访问器等
    public string Name => "小米喂大象"; //只读属性
    public void SetAge(int age) => Age = age; //方法
    public void Log(string msg) => System.Console.WriteLine($"{Name} : {msg}");
    
    public void Init(string name, string password, int age) {
        Name = name;
        Password = password;
        Age = age;
    }
    public User(string name) => Init(name, "", 18); //构造函数
    public User(string name, string password) => Init(name, password, 18); 
    public ~User() => System.Console.WriteLine("Finalized"); //析构函数(仅示例)
    
    public string password;
    public int Password{ //属性访问器
        get => password;
        set => SetPassword(value);
    }

    Throw expressions (异常表达式)

    // C#7.0
    // 1. c#7.0以前,throw是一个语句,因为是语句,所以在某些场合不能使用。
    //    包括条件表达式、空合并表达式、Lambda表达式等。
    // 2. C#7.0可以使用了, 语法不变,但是可以放置在更多的位置
    public string Name {
        get => name;
        set => name = value ?? 
         throw new ArgumentNullException("Name must not be null");
    }

    Generalized async return types(更泛化的异步返回类型)

    // C#7.0
    // 1. 7.0以前异步方法只能返回void、Task、Task<T>,现在允许定义其他类型来返回
    // 2. 主要使用情景:从异步方法返回Task引用,需要分配对象,某些情况下会导致性能问题。
    //        遇到对性能敏感问题的时候,可以考虑使用ValueTask<T>替换Task<T>。

    Numeric literal syntax improvements(数值字面量语法改进)

    // C#7.0
    // c#7.0为了增加数字的可读性,增加了两个新特性:二进制字面量(ob开头)、数字分隔符(_)
    int b = 123_456_789; // 作为千单位分隔符
    int c = 0b10101010; // 增加了表示二进制的字面量, 以0b开头
    int d = 0b1011_1010; // 二进制字面量里加入数字分割符号_
    float e = 3.1415_926f; // 其他包括float、double、decimal同样可以使用
    double f = 1.345_234_322_333_567_222_777d;
    decimal g = 1.618_033_988_749_894_586_834_365_638_117_720_309_179M;
    long h = 0xff_42_90_b8; // 在十六进制中使用

    C#7.1新特性

    C#7.1是c#的第一个带小数点的版本,意味着快速迭代与发布
    一般需要在编译器里设置语言版本才能使用

    Async main (异步Main方法)

    // C#7.0中async不能用于main方法,7.1可以
    
    // 以前
    static int Main() {
        return DoAsyncWork().GetAwaiter().GetResult();
    }
    
    // 现在
    static async Task<int> Main() {
        return await DoAsyncWork();
    }
    static async Task Main() { // 没有返回
        await SomeAsyncMethod();
    }

    Default literal expressions (default字面量表达式)

    // 1. C#7.1以前给一个变量设置缺省值,需要使用default(T), C#7.1因为可以推断表达式
    //    的类型,所以可以直接使用default字面量,编译器推断出与default(T)一样的值
    // 2. default字面量可用于以下任意位置:
    //       变量初始值设定项
    //       变量赋值
    //       声明可选参数的默认值
    //       为方法调用参数提供值
    //       返回语句
    //       expression in an expression bodied member
    //      (使用表达式方法体的成员中的表达式)
    
    public class User {
        public string Name { get; set; } = default;
        public int Age { get; set; } = default;
        public int Score => default;
    }
    public static int Test(string name, int age, int score = default) {
        // 以前
        string s1 = default(string);
        var s2 = default(string);
        int i = default(int);
        User u = default(User);
    
        // 现在
        string s3 = default;
        string s4 = "hello";
        s4 = default;
    
        return default;
    }
    
    Test(default, default);
    

    Inferred tuple element names(tuple元素名可推导)

    // c#7.0引入tuple,7.1增强了tuple中元素的命名,可通过推导来完成tuple中元素的命名
    // 使用变量来初始化tuple时,可以使用变量名给tuple中元素命名
    var name = "xm01";
    var age = 18;
    var p = (name:name, age:18); // 以前,显式命名
    var p2 = (name, age); // 现在,可以推导来命名
    p2.age = 19;

    Reference assembly generation

    编译器添加了两个新的选项用于控制引用程序集的生成:-refout 和 -refonly
    一个表示只引用,一个表示需要输出引用(故需要指定路径)


    C#7.2

    Reference semantics with value types(只读引用)

    1. 使用值类型变量时,通过可避免堆分配,但需要进行一次复制操作;
    2. 为了取得折中效果, C#7.2提供了一个机制:似值类型不可被修改,但按引用传递。
    // 这几种情况可以:
    // 1. in修饰符修饰的参数,不可被调用的方法修改;
    // 2. ref readonly方式返回一个值,不能被修改;
    // 3. readonly struct声明一个结构,可以作为in参数传递
    // 4. ref struct 声明一个结构,指示直接访问托管内存,始终分配有堆栈。

    Non-trailing named arguments(命名参数不需要在最后)

    1. C#7.2以前,命名参数后面不能再跟位置参数
    2. C#7.2以后,只要命名参数位置正确,可以和位置参数混用
    3. 这样做的目的是: 使用名字参数的调用,一眼可以看出来这个参数的含义,
      例如参数是一个boolean型的参数,写代码时直接传true,根本看不出什么含义,这时候写上名字可以明确调用接口

    Leading underscores in numeric literals(数字字面量的前导分隔符)

    // 1. C#7.0中提供了下划线来分割数字字面量,以提高可读性,
    //    但是 下划线分割符(_) 不可作为字面量的第一个字符。
    // 2. c#7.2中允许十六进制字面量和二进制字面量以_开头
    
    // 以前
    int x = 0b1011_1010; 
    long y = 0xff_42_90_b8;
    
    // 现在
    int x = 0b_1011_1010; 
    long y = 0x_ff_42_90_b8;
    

    private protected access modifier (private protected 访问修饰符)

    1. C#7.2添加了一个private protected 访问修饰符
    2. 表示:
      1)只有自己访问;
      2)派生类也可以访问,但仅限于在同一个程序集的派生类
    展开全文
  • C# 1.0 到 C# 9.0,历代 C# 语言特性一览

    千次阅读 热门讨论 2021-02-06 12:25:09
    本文内容C# 版本历史记录C# 版本特性说明C# 7.0元组和弃元更多的 expression-bodied 成员`out`变量异步 `Main` 方法模式匹配引发表达式C# 8.0默认接口方法异步流索引和范围C# 9.0Record顶级语句模式匹配增强参考链接...
  • Mads Torgersen介绍C# 7及后续版本新特性.pdf

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 157,435
精华内容 62,974
关键字:

7c#新特性

c# 订阅