• C# – 11.0


    前言

    .NET 7 来到 RC 阶段了. C# 11 也伴随而来咯, 这篇看看有哪些可能会用上的好功能呗.

    参考

    What's new in C# 11

    Generic attributes

    参考: Docs – Generic attributes

    在 C# 11.0 以前, Attribute 是无法使用 Generic 的. 

    当需要传递类型时, 只能给予 Type, 然后通过反射达到目的, 这样就少了类型提示.

    C# 11.0 支持了这个功能

    before

    public class Animal {
        public string Name { get; set; } = "";
    }
    
    [AttributeUsage(AttributeTargets.Class)]
    public class TestAttribute : Attribute
    {
        public TestAttribute(Type type) { }
    }
    
    [Test(typeof(Animal))]
    public class Person
    {
    
    }

    after

    public class Animal {
        public string Name { get; set; } = "";
    }
    
    [AttributeUsage(AttributeTargets.Class)]
    public class TestAttribute<T> : Attribute where T : Animal
    {
        
    }
    
    [Test<Animal>()]
    public class Person
    {
    
    }

    limitation

    虽然说支持 Generic, 但也不是完全. 过往可以用 typeof(AnyType) 的地方都可以替换成 Generic, 但是如果 typeof 不支持的, 那就不行了.

    比如 typeof(string?), typeof(dynamic)

    Newlines in string interpolations

    参考: Docs – Newlines in string interpolations

    在 C# – 6.0, 7.0, 8.0, 9.0 总结 有提到过. 6.0 的功能, 但它有一些 limitation. 11.0 算是优化了一些.

    before

    不能使用 new line, 除非加上 @

    var value = $@"{
        (true ? "a" : "b")
    }";

    after

    11.0 支持了 new line. 但也仅限于花括弧 {}, string 依然不可以 new line 需要配合 @ 才行

    var value = $"{
        (true ? "a" : "b")
    }";

    Raw string literals

    参考: Docs – Raw string literals

    C# 写 JSON 是很难看的.

    before

    var json1 = @"{
        ""name"" : ""Derrick""  
    }";
    var json2 = "{ \"name\": \"Derrick\" }";

    要嘛用反斜杠 \", 要嘛用 @ double ", 不管这样都破坏了内容

    C# 11.0 有了新语法

    after

    var json = """
        { 
            "name" : "Derrick",
            "age" : 10
        }
    """;

    使用 triple ", 不需要反斜杠, 也不需要 @ 了.

    它的好处就是只破坏了开头和结尾, 但内容是正确的. 

    p.s. 花括弧必须是 new line 哦, 像下面这样是错误的. 

    另外, 你甚至可以用更多的 ", 比如 4 个 """" .... """" 那么, 内容就可以使用到 triple """. 这个设计相当灵活. (开头结尾最少是 triple ", double 不够哦)

    配搭 interpolations

    var json = $$"""
        { 
            "name" : "{{ value }}",
            "age" : 10
        }
    """;

    double $ 表示, 当出现 double {{ }} 的时候才是 interpolations. 也可以声明更多, 比如 triple $ 对应的就是 {{{ }}} 才是 interpolations.

    这个写法估计未来会成为主流, 以后应该不再需要使用 @, double ", 反斜杠 \ 了.

    List patterns

    参考: Docs – List patterns

    在 C# – 10.0 我有提到了 Pattern Matching 的使用.

    C# 11.0 提升了 matching 的能力, 现在连 Array List 都可以 matching 了.

    它有点像 TypeScript 的 extend 配上 Tuple

    simple match (constant pattern)

    var values = new string[] { "a", "b", "c" };
    var yes = values is ["a", "b", "c"]; // true

    List 也是可以

    var values = new List<string> { "a", "b", "c" };
    var yes = values is ["a", "b", "c"]; // true

    greater than match

    除了简单的 equal match, 其它 operator 也是可以拿来 match 的, 比如 >=, 是不是很牛?

    var values = new List<int> { 1, 2, 3 };
    var yes = values is [1, 2, >= 3]; // true

    skip single match (discard pattern)

    underscore 表达 skip 掉, 不管什么值都可以

    var values = new List<string> { "a", "b", "c" };
    var yes = values is ["a", _, "c"]; // true

    skip multiple match (range pattern)

    通过 double dot 点点, 可表达 skip 掉 0 到多个 值匹配.

    var values = new List<int> { 1, 2, 3, 4 };
    var yes = values is [1, .., >= 4]; // true

    上面这个表示, 开头是 1, 最后是 >=4, 中间是什么, 有没有, 有多少值都无所谓.

    get var from list (var pattern)

    通过 var 关键字, 可以提取变量. 类似 TypeScript 的 infer.

    var values = new List<int> { 1, 2, 3 };
    if (values is [var first, _, >= 3]) {
        Console.WriteLine(first); // 1
    }

    还可以拿 multiple values 哦

    var values = new[] { 1, 2, 3, 4, 5 };
    if (values is [1, 2, .. var vals])
    {
        Console.WriteLine(vals);
    }

    limitation

    List 无法拿 multiple values

    match 和 var 不可以同时使用

    Required Property

    当我们声明一个 property setter = init 时, 如果没有给予一个 Constructor 或者 default value 那么它会出现提示.

    before

    解决方式

    public class Person
    {
        public Person() 
        {
            Name = ""; // 1
        }
        public string Name { get; init; } = ""; // 2
        public string Name { get; init; } = null!; // 3
    }

    这 3 种写法都可以移除提示. 但是呢, 这不够好. 因为 setter init 只要在 new Class 的时候给予值就可以了.

    所以下面这个方式也应该可以移除提示才对.

    但很遗憾 C# 11.0 之前没有任何方法做到这个.

    after

    在 C# 11.0 多了一个叫 required 的关键字

    当声明 required 以后, 在 new Class 的时候如果没有给予 init value 那么就会报错.

    像这样给予 init value 后就不会报错了.

    var person = new Person { 
        Name = ""
    };

    另外, required 并不能靠 Construtor 和 default value 去除哦 

    所以 required 并不是拿来替代原本的方案的. 它应该只用来声明那些在 new Class 需要给予 init value 的 property.

    SetsRequiredMembers Attribute 可以用来声明 Constructor 给予了所有 required property 值. (我目前还没有明白它的使用场景. 以后再补上)

    public class Person
    {
        [SetsRequiredMembers]
        public Person() 
        {
            Name = "dada";
        }
    
        public required string Name { get; init; }
    }
    
    public class Program
    {
        public static void Main() 
        {
            var person = new Person(); // 不报错
        }
    }

    Regular Expression Improvements (.NET 7)

    before

    var numberRegex = new Regex("\\d");
    var matched1 = numberRegex.Match("123").Success; // true
    var matched2 = numberRegex.Match("abc").Success; // false

    现在 Visual Studio 会提示不要这样写, 要改成 compile time 写法

    after

    public static partial class Program
    {
        public static void Main()
        {
            var numberRegex = NumberRegex();
            var matched1 = numberRegex.Match("123").Success; // true
            var matched2 = numberRegex.Match("abc").Success; // false
        }
    
        [GeneratedRegex("\\d")]
        private static partial Regex NumberRegex();
    }

    注: 只有这种 hardcode 的正则可以用新写法, 如果是拼接的就只能 runtime

    欲知详情看这篇: Regular Expression Improvements in .NET 7

    INumber (.NET 7) & Interface Static Abstract

    参考: YouTube – The weirdest C# 11 feature but also the best one

    假设有一个方法, 参数是 int, 那么变量 double 就无法放进去

    C# 有很多数字类型, double, float, int. 那有没有一个抽象的写法呢? 不管什么 number 都都可以调用这个方法, 然后累加 1 并返回.

    public static void Main() 
    {
        double age = 10d;
        var newAge = Method(age);
    
        T Method<T>(T age) where T : INumber<T> {
            return age + T.One;
        }
    }

    首先是把 int 换成 generic T, 然后声明 T 必须是 INumber (这个是 .NET 7 才有的)

    interface static abstract

    return age + 1 是不 ok 的, 必须利用 C# 11.0 的新特性 T.One

    它是 interface static abstract property, 大概长这样

    interface MyInterface<T> 
    { 
        static abstract T Value { get; }
    }

    实现 MyInterface 接口的类, 必须实现 static T Value. 于此同时 generic 就可以像 T.One 那样调用接口的静态函数了.

    LINQ Order & OrderDescending (.NET 7)

    只是一个语法糖

    public static void Main() 
    {
        var values = new int[] { 2, 5, 1, 4, 3 };
            
        var before1 = values.OrderBy(v => v);
        var after1 = values.Order(); // 1, 2, 3, 4, 5
    
        var before2 = values.OrderByDescending(v => v);
        var after2 = values.OrderDescending(); // 5, 4, 3, 2, 1
    }

    Microseconds and Nanoseconds (.NET 7)

    参考:

    Microseconds and Nanoseconds available in .NET 7

    Docs – Adding Microseconds and Nanoseconds to TimeStamp, DateTime, DateTimeOffset, and TimeOnly

    milli, micro, nano, tick

    先讲讲基础, 我一路以来都只处理到 millisecond 而已, 没有玩过 micro 和 nano.

    1 second = 1000 milliseconds 毫秒

    1 millisecond = 1000 microsecond 微秒

    1 microsecond = 1000 nanoseconds 纳秒

    1 tick = 100 nanoseconds

    1 microsecond = 10 tick

    Get micro, nano from tick

    在 .NET 7 以前, 要处理 micro 和 nano 只能依靠 tick.

    var date1 = new DateTimeOffset(2022, 10, 3, 16, 46, 23, 123, TimeSpan.FromHours(8)); // 初始化只能设置到 Millisecond 哦
    var date = DateTimeOffset.Parse("2022-10-03T16:46:23.123456793Z"); // 解析可以到 8 位而已, 后面无视, 同时第 8 位会拿来进位, 所以最终是 123ms 456micro, 800ns 相等于 1234568 tick
    var tick = date.Ticks; // 638004123831234568 (两千多年换算成 tick, 不是 1970 开始哦)
    
    var tickOnly = date.Ticks % TimeSpan.TicksPerSecond; // 1234568
    var microTick = date.Ticks % TimeSpan.TicksPerMillisecond; // 4568
    var nanoTick = date.Ticks % (TimeSpan.TicksPerMillisecond / 1000); // 8
    
    var micro = (microTick - nanoTick) / 10; // 456
    var nano = nanoTick * 100; // 800

    通过各做转换技术, 最终就得到 micro 和 nano 了. 如果要做 +- 那就 date.AddTicks(nanoTick);

    .NET 7

    var date1 = new DateTimeOffset(2022, 10, 3, 16, 46, 23, 123, 456, TimeSpan.FromHours(8)); // 初始化可以到 Microsecond, 但 Nanosecond 不可以哦
    date1 = DateTimeOffset.Parse("2022-10-03T16:46:23.123456793Z"); // 和之前一样
    var micro = date1.Microsecond; // 456
    var nano = date1.Nanosecond; // 800
    date1.AddMicroseconds(-micro); // 有 AddMicroseconds, 但没有 AddNanoseconds 哦

    比之前好多了, 只是 nano 支持的不完整, 不知道为什么...

  • 相关阅读:
    【算法总结】多项式相关
    【算法总结】积性函数相关
    【算法总结】概率与期望相关
    【算法总结】博弈论相关
    【算法总结】线性代数相关
    【算法总结】根号算法相关
    【算法总结】计算几何相关
    【算法总结】组合数学相关
    【算法总结】字符串相关
    【算法总结】数论相关
  • 原文地址:https://www.cnblogs.com/keatkeat/p/16727172.html
Copyright © 2020-2023  润新知