zoukankan      html  css  js  c++  java
  • C#新特性

     条件 ref 表达式

    条件表达式可能生成 ref 结果而不是值。 例如,你将编写以下内容以检索对两个数组之一中第一个元素的引用

    ref var r = ref (arr != null ? ref arr[0] : ref otherArr[0]);

     ref struct

     ref 结构类型的实例在堆栈上分配,并且不能转义到托管堆,因此不会给 GC 带来任何的压力。相对的,使用中就会有不能逃逸出栈的强制限制。

    编译器将 ref 结构类型的使用限制如下:

    • ref 结构不能是数组的元素类型。
    • ref 结构不能是类或非 ref 结构的字段的声明类型。
    • ref 结构不能实现接口。
    • ref 结构不能被装箱为 System.ValueType 或 System.Object
    • ref 结构不能是类型参数。
    • ref 结构变量不能由 lambda 表达式本地函数捕获。
    • ref 结构变量不能在 async 方法中使用。 但是,可以在同步方法中使用 ref 结构变量,例如,在返回 Task 或 Task<TResult> 的方法中。
    • ref 结构变量不能在迭代器中使用。

    Span<T> 和 System.ReadOnlySpan<T> 本身是一个 readonly ref struct,成功的封装出了安全且高性能的内存访问操作,且可在大多数情况下代替指针而不损失任何的性能。

    ref struct MyStruct
    {
        public int Value { get; set; }
    }
        
    class RefStructGuide
    {
        static void Test()
        {
            MyStruct x = new MyStruct();
            x.Value = 100;
            Foo(x); // ok
            Bar(x); // error, x cannot be boxed
        }
    
        static void Foo(MyStruct x) { }
        
        static void Bar(object x) { }
    }

    通过 stackalloc 直接在栈上分配内存,并使用 Span<T> 来安全的访问,同样的,这么做可以做到 0 GC 压力。

    stackalloc 允许任何的值类型结构,但是要注意,Span<T> 目前不支持 ref struct 作为泛型参数,因此在使用 ref struct 时需要直接使用指针。

    static unsafe void RefStructAlloc()
        {
            MyStruct* x = stackalloc MyStruct[10];
            for (int i = 0; i < 10; i++)
            {
                *(x + i) = new MyStruct { Value = i };
            }
        }
    
        static void StructAlloc()
        {
            Span<int> x = stackalloc int[10];
            for (int i = 0; i < x.Length; i++)
            {
                x[i] = i;
            }
        }

    性能敏感时对于频繁调用的函数使用 SkipLocalsInit

    C# 为了确保代码的安全会将所有的局部变量在声明时就进行初始化,无论是否必要。一般情况下这对性能并没有太大影响,但是如果你的函数在操作很多栈上分配的内存,并且该函数还是被频繁调用的,那么这一消耗的副作用将会被放大变成不可忽略的损失。

    因此你可以使用 SkipLocalsInit 这一特性禁用自动初始化局部变量的行为。

    [SkipLocalsInit]
    unsafe static void Main()
    {
        Guid g;
        Console.WriteLine(*&g);
    }

    上述代码将输出不可预期的结果,因为 g 并没有被初始化为 0。另外,访问未初始化的变量需要在 unsafe 上下文中使用指针进行访问。

    表达式体

    成员函数、只读属性、构造函数、get 和 set 访问器。

    // Expression-bodied constructor
    public ExpressionMembersExample(string label) => this.Label = label;
    
    private string label;
    
    // Expression-bodied get / set accessors.
    public string Label
    {
        get => label;
        set => this.label = value ?? "Default label";
    }

    throw 表达式

    throw 可以用作表达式和语句。 这允许在以前不支持的上下文中引发异常。 这些方法包括条件表达式、null 合并表达式和一些 lambda 表达式。

    string arg = args.Length >= 1 ? args[0] :
                                  throw new ArgumentException("You must supply an argument");
    
    name = value ??
            throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null");
    
    DateTime ToDateTime(IFormatProvider provider) =>
             throw new InvalidCastException("Conversion to a DateTime is not supported.");

    ValueTask

    从异步方法返回 Task 对象可能在某些路径中导致性能瓶颈。 Task 是引用类型,因此使用它意味着分配对象。

    ValueTask 此增强功能对于库作者最有用,可避免在性能关键型代码中分配 Task

    需要添加 NuGet 包 System.Threading.Tasks.Extensions 才能使用 ValueTask<TResult> 类型。

    数字文本语法改进

    二进制文本和数字分隔符 

    public const int OneHundredTwentyEight = 0b1000_0000;
    
    public const long BillionsAndBillions = 100_000_000_000;
    
    public const double AvogadroConstant = 6.022_140_857_747_474e23;

    int binaryValue = 0b_0101_0101;

     

    默认文本表达式

    针对默认值表达式的一项增强功能。 这些表达式将变量初始化为默认值。 过去会这么编写:

    Func<string, bool> whereClause = default(Func<string, bool>);

    现在,可以省略掉初始化右侧的类型:

    Func<string, bool> whereClause = default;

    增强的泛型约束

    可以将类型 System.Enum 或 System.Delegate 指定为类型参数的基类约束。

    现在也可以使用新的 unmanaged 约束来指定类型参数必须是不可为 null 的“非托管类型”。
    非托管类型 :
    • sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal 或 bool
    • 任何枚举类型
    • 任何指针类型
    • 任何用户定义的 struct 类型,只包含非托管类型的字段

        public struct Coords<T> where T : unmanaged
        {
            public T X;
            public T Y;
        }
    
            var c = new Coords<int>();

    默认接口方法

    可以将成员添加到接口,并为这些成员提供实现。

    异步流

    public static async System.Collections.Generic.IAsyncEnumerable<int> GenerateSequence()
    {
        for (int i = 0; i < 20; i++)
        {
            await Task.Delay(100);
            yield return i;
        }
    }
    
    await foreach (var number in GenerateSequence())
    {
        Console.WriteLine(number);
    }

    索引和范围

     0 索引与 sequence[0] 相同。 ^0 索引与 sequence[sequence.Length] 相同。 请注意,sequence[^0] 不会引发异常

    对于任何数字 n,索引 ^n 与 sequence.Length - n 相同。^1 索引最后

     [..]表示整个范围,相当于范围 [0..^0] 

    words[1..4]包括 words[1] 到 words[3]。 元素 words[4] 不在该范围内

    var allWords = words[..]; // contains "The" through "dog".
    var firstPhrase = words[..4]; // contains "The" through "fox"
    var lastPhrase = words[6..]; // contains "the", "lazy" and "dog"

     

    Null 合并赋值

    List<int> numbers = null;
    int? i = null;
    
    numbers ??= new List<int>();
    numbers.Add(i ??= 17);
    numbers.Add(i ??= 20);
    
    Console.WriteLine(string.Join(" ", numbers));  // output: 17 17
    Console.WriteLine(i);  // output: 17
  • 相关阅读:
    Instant Python 中文缩减版
    《Java疯狂讲义》(第3版)学习笔记 2
    《Java疯狂讲义》(第3版)学习笔记 1
    NXP Mifare S50标准IC卡- 访问位(Access Bits) 分析
    Python中获取异常(Exception)信息
    支持向量机(SVM)入门
    棋类游戏中人机博弈的设计
    (翻译)如何对python dict 类型按键(keys)或值(values)排序
    Python实现打印二叉树某一层的所有节点
    FIFA halts 2026 bids amid scandal 国际足联在丑闻期间停止2026年足球世界杯申请
  • 原文地址:https://www.cnblogs.com/yetsen/p/13442305.html
Copyright © 2011-2022 走看看