从未来看 C#

前言

如今 C# 虽然发展到了 8.0 版本,引入了诸多的函数式特性,但其实在 C# 未来的规划当中,还有很多足以大规模影响现有 C# 代码结构和组成的特性,本文中将会对就重要的特性进行介绍,并用代码示例展示这些特性。

以下特性将会在 C# 9.0、10.0 或者更高版本提供。

Records

Records 是一种全新的简化的 C# classstruct 的形式。

现在当我们需要声明一个类型用来保存数据,并且支持数据的解构的话,需要像如下一样写出大量的样板代码:

class Point : IEquatable<Point>
{
    public readonly double X;
    public readonly double Y;

    public Point(double X,double Y)
    {
        this.X = X;
        this.Y = Y;
    }

    public static bool operator==(Point left,Point right) { ... }

    public bool Equals(Point other) { ... }
    public override bool Equals(object other) { ... }
    public override int GetHashCode() { ... }
    public void Deconstruct(out double x,out double y) { ... }
}

十分复杂。引入 Records 之后,上面的样板代码只需简化成一句话:

data class Point(double X,double Y);

并且 Records 支持数据的变换、解构和模式匹配:

var pointA = new Point(3,5);
var pointB = pointA with { Y = 7 };
var pointC = new Point(3,7);

// 当 Y = 5 时为 X,否则为 Y
var result = pointB switch
{
    (var first,5) => first,(_,var second) => second
}; 

// true
Console.WriteLine(pointB == pointC);

当然,recordimmutable 的,并且是可以合并(继承)的,也可以标记为 sealed 或者 abstract

sealed data class Point3D(double X,double Y,double Z) : Point(X,Y);

上面的这种 record 声明方式是基于位置声明的,即 Point(first,second)fisrt 所代表的第一个位置将成为 Xsecond 所代表的第二个位置将成为 Y

还有一种声明方式是基于名称的:

data class Point { double X; double Y };
var point = new Point { X = 5,Y = 6 };

Discriminated Unions

Discriminated unions 又叫做 enum class,这是一种全新的类型声明方式,顾名思义,是类型的 “枚举”。

例如,我们需要定义形状,形状有矩形、三角形和圆形,以前我们需要先编写一个 Shape 类,然后再创建 RectangleTriangleCircle 类继承 Shape 类,现在只需要几行就能完成,并且支持模式匹配和解构:

enum class Shape
{
    Retangle(double Width,double Height);
    Triangle(double Bottom,double Height);
    Circle(double Radius);
    Nothing;
}

然后我们就可以使用啦:

var circle = new Circle(5);
var rec = new Rectangle(3,4);

if (rec is Retangle(_,4))
{
    Console.WriteLine("这不是我想要的矩形");
}

var height = GetHeight(rec);

double GetHeight(Shape shape)
    => shape switch
    {
        Retangle(_,height) => height,Triangle(_,_ => throw new NotSupportedException()
    };

利用此特性,我们可以轻而易举的实现支持模式匹配的、type sound 的可空数据结构:

enum class Option<T>
{
    Some(T value);
    None;
}

var x = Some(5);
// Option<never>
var y = None;

void Foo(Option<T> value)
{
    var bar = value switch
    {
        Some(var x) => x,None => throw new NullReferenceException()
    };
}

Union and Intersection Types

当我们想要表示一个对象是两种类型其一时,将可以使用联合类型来表达:

public type SignedNumber = short | int | long | float | double | decimal;
public type ResultModel<T> = DataModel<T> | ErrorModel;

这在 Web API 中非常有用,当我们的接口可能返回错误的时候,我们不再需要将我们的数据用以下方式包含在一个统一的模式中:

public class ResultModel<T>
{
    public string Message { get; set; }
    public int Code { get; set; }
    public T Data { get; set; }
}

我们将能够做到,不依赖异常等流程处理的方式做到错误时返回错误信息,请求正常处理时返回真实所需的数据:

public async ValueTask<DataModel | ErrorModel> SomeApi()
{
    if (...) return new DataModel(...);
    return new ErrorModel(...);
}

还有和类型,用来表示多个类型之和,我们此前在设计接口时,如果需要一个类型实现了多个接口,则需要定义一个新接口去实现之前的接口:

interface IA { ... }
interface IB { ... }
interface IAB : IA,IB { }

void Foo(IAB obj) { ... }

有了和类型之后,样板代码 IAB 将不再需要:

void Foo(IA & IB obj) { ... }

或者我们也可以这样声明新的类型:

type IAB = IA & IB;

Bottom Type

Bottom type 是一种特殊的类型 nevernever 类型是任何类型的子类,因此不存在该类型的子类。一个 never 类型的什么都不表示。

Union types 带来一个问题,就是我们有时候需要表达这个东西什么都不是,那么 never 将是一个非常合适的选择:

type Foo = Bar | Baz | never;

另外,never 还有一个重要的用途:控制代码流程,一个返回 never 的函数将结束调用者的逻辑,即这个函数不会返回:

void | never Foo(int x)
{
    if (x > 5) return;
    return never;
}

void Main()
{
    Foo(6);
    Console.WriteLine(1);
    Foo(4);
    Console.WriteLine(2);
}

上述代码将只会输出 1。

Concepts

Concepts 又叫做 type classes、traits,这个特性做到可以在不修改原有类型的基础上,为类型实现接口。

首先我们定义一个 concept

concept Monoid<T>
{
    // 加函数
    T Append(this T x,T y);
    // 零属性
    static T Zero { get; }
}

然后我们可以为这个 concept 创建类型类的实例:

instance IntMonoid : Monoid<int>
{
    int Append(this int x,int y) => x + y;
    static int Zero => 0;
}

这样我们就为 int 类型实现了 Monoid<int> 接口。

当我们想实现一个函数用来将一个 int 数组中的所有元素求和时,只需要:

public T Sum<T,inferred M>(T[] array) where M : Monoid<T>
{
    T acc = M.Zero;
    foreach (var i in array) acc = acc.Append(i);
    return acc;
}

注意到,类型 M 会根据 T 进行自动推导得到 Monoid<int>

这样我们就能做到在不需要修改 int 的定义的情况下为其实现接口。

Higher Kinded Polymorphism

Higher kinded polymorphism,又叫做 templated template,或者 generics on generics,这是一种高阶的多态。

举个例子,比如当我们需要表达一个类型是一个一阶泛型类型,且是实现了 ICollection<> 的容器之一时,我们可以写:

void Foo<T>() where T : <>,ICollection<>,new();

有了这个特性我们可以轻而易举的实现 monads

例如我们想要做一个将 IEnumerable<> 中所有元素变成某种集合类型的时候,例如 ToList() 等,我们就不需要显式地实现每一种需要的类型的情况(例如 List<>):List<T> ToList(this IEnumerable<T> src)了。

我们只需要这么写:

T<X> To<T,X>(this IEnumerable<X> xs) where T : <>,new()
{
    var result = new T<X>();
    foreach (var x in xs) result.Add(x);
    return result;
}

当我们想要把一个 IEnumerable<int> x 转换成 List<int> 时,我们只需简单的调用:x.To<List<>>() 即可。

Simple Programs

该特性允许编写 C# 代码时,无需 Main 函数,直接像写脚本一样直接在文件中编写逻辑代码,以此简化编写少量代码时却需要书写大量样板代码的问题:

以前写代码:

namespace Foo
{
    class Bar
    {
        static async Task Main(string[] args)
        {
            await Task.Delay(1000);
            Console.WriteLine("Hello world!");
        }
    }
}

现在写代码:

await Task.Delay(1000);
Console.WriteLine("Hello world!");

Expression Blocks

该特性允许创建表达式块:

Func<int,int,bool> greaterThan = (a,b) => if (a > b) a else b;

// true
greaterThan(5,4);

因此有了以上特性,我们可以利用表达式实现更加复杂的东西。

后记

以上特性都是对代码布局和组成影响非常大的特性,并且不少特性几年前就已经被官方实现,但是因为存在尚未讨论解决的问题,迟迟没有发布进产品。

除此之外,还有几十个用于改进语言和方便用户使用等等的小特性也在未来的规划当中,此处不进行介绍。

未来的 C# 和今天的 C# 区别是很大的,作为一门多范式语言,C# 正在朝远离 Pure OOP 的方向渐行渐远,期待这门语言变得越来越好。

原文地址:https://www.cnblogs.com/hez2010

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


本文将从上往下,循序渐进的介绍一系列相关.NET的概念,先从类型系统开始讲起,我将通过跨语言操作这个例子来逐渐引入一系列.NET的相关概念,这主要包括:CLS、...
基于 .NET 的一个全新的、好用的 PHP SDK + Runtime: PeachPie 来啦!
.NET 异步工作原理介绍。
引子 .NET 6 开始初步引入 PGO。PGO 即 Profile Guided Optimization,通过收集运行时信息来指导 JIT 如何优化代码,相比以前没有 PGO 时可以做更多以前难以
前言 2021/4/8 .NET 6 Preview 3 发布,这个版本的改进大多来自于底层,一起来看看都有什么新特性和改进吧。 库改进 新增值类型作为字典值时更快的处理方法 .NET 6 Previ
前言 开头防杠:.NET 的基础库、语言、运行时团队从来都是相互独立各自更新的,.NET 6 在基础库、运行时上同样做了非常多的改进,不过本文仅仅介绍语言部分。 距离上次介绍 C# 10 的特性已经有
直接使用 CIL - .NET 上的汇编语言编写 .NET Standard 类库
前言 不知不觉中,.NET Framework 已经更新到 4.8,.NET Core 也更新到了 3.0 版本。那么 .NET 的未来怎么样呢? 计划 2019 年 Build 大会上,微软宣布下一
本文带你穿越到未来一起看看未来的 C# 到底长什么样子。
前言 TypedocConverter 是我先前因帮助维护 monaco-editor-uwp 但苦于 monaco editor 的 API 实在太多,手写 C# 的类型绑定十分不划算而发起的一个项
前言 在 2021 年 3 月 11 日, .NET 6 Preview 2 发布,这次的改进主要涉及到 MAUI、新的基础库和运行时、JIT 改进。 .NET 6 正式版将会在 2021 年 11
前言 命名空间已经在 .NET 中使用了多年,一直追溯到 .NET Framework 1.1。它在 .NET 实施本身的数百个位置中使用,并且直接被成千上万个应用程序使用。在所有这些方面,它也是 C
.NET 上的统一跨平台 UI 框架来啦
使用 F# 手写一个 Typedoc 转 C# 代码生成器,方便一切 C# 项目对 TypeScript 项目的封装。
LINQ + SelectMany = Monad!
C# 10 主要特性一览
C# 的编译期反射终于来啦!
前言 2021 年 2 月 17 日微软发布了 .NET 6 的 Preview 1 版本,那么来看看都有什么新特性和改进吧,由于内容太多了因此只介绍一些较为重点的项目。ASP.NET Core 6
前言 有一个东西叫做鸭子类型,所谓鸭子类型就是,只要一个东西表现得像鸭子那么就能推出这玩意就是鸭子。 C 里面其实也暗藏了很多类似鸭子类型的东西,但是很多开发者并不知道,因此也就没法好好利用这些东西,
经过五年半的持续维护,Senparc.Weixin SDK 逐步丰满和完善,在升级的过程中,我们为基础库(Senparc.Weixin.dll)加入了许多通用的功能,例如加密/解密算法、通用缓存方法等