例如,你想定义一个可加而且有零的接口 IMonoid<T>:
interface IMonoid<T> where T : IMonoid<T> { abstract static T Zero { get; } abstract static T operator+(T l, T r); }然后可以对其进行实现,例如这里的 MyInt:
public class MyInt : IMonoid<MyInt> { public MyInt(int val) { Value = val; } public static MyInt Zero { get; } = new MyInt(0); public static MyInt operator+(MyInt l, MyInt r) => new MyInt(l.Value + r.Value); public int Value { get; } }然后就能写出一个方法对 IMoniod<T> 进行求和了,这里为了方便写成扩展方法:
public static class IMonoidExtensions { public static T Sum<T>(this IEnumerable<T> t) where T : IMonoid<T> { var result = T.Zero; foreach (var i in t) result += i; return result; } }最后调用:
List<MyInt> list = new() { new(1), new(2), new(3) }; Console.WriteLine(list.Sum().Value); // 6你可能会问为什么要引入一个 System.Runtime.Experimental,因为这个包里面包含了 .NET 基础类型的改进:给所有的基础类型都实现了相应的接口,比如给数值类型都实现了 INumber<T>,给可以加的东西都实现了 IAdditionOperators<TLeft, TRight, TResult> 等等,用起来将会非常方便,比如你想写一个函数,这个函数用来把能相加的东西加起来:
T Add<T>(T left, T right) where T : IAdditionOperators<T, T, T> { return left + right; }就搞定了。
接口的静态抽象方法支持和未来 C# 将会加入的 shape 特性是相辅相成的,届时 C# 将利用 interface 和 shape 支持 Haskell 的 class、Rust 的 trait 那样的 type classes,将类型系统上升到一个新的层次。
泛型 attribute是的你没有看错,C# 的 attributes 支持泛型了:
class TestAttribute<T> : Attribute { public T Data { get; } public TestAttribute(T data) { Data = data; } }然后你就能这么用了:
[Test<int>(3)] [Test<float>(4.5f)] [Test<string>("hello")] 允许在方法上指定 AsyncMethodBuilderC# 10 将允许方法上使用 [AsyncMethodBuilder(...)] 来使用你自己实现的 async method builder,代替自带的 Task 或者 ValueTask 的异步方法构造器。这也有助于你自己实现零开销的异步方法。
line 指示器支持行列和范围以前 #line 只能用来指定一个文件中的某一行,现在可以指定行列和范围了,这对写编译器和代码生成器的人非常有用:
#line (startLine, startChar) - (endLine, endChar) charOffset "fileName" // 比如 #line (1, 1) - (2, 2) 3 "test.cs" 嵌套属性模式匹配改进以前在匹配嵌套属性的时候需要这么写:
if (a is { X: { Y: { Z: 4 } } }) { ... }现在只需要简单的:
if (a is { X.Y.Z: 4 }) { ... }就可以了。
改进的字符串插值以前 C# 的字符串插值是很粗暴的 string.Format,并且对于值类型参数来说会直接装箱,对于多个参数而言还会因此而分配一个数组(比如 string.Format("{} {}", a, b) 其实是 string.Format("{} {}", new object [] { (object)a, (object)b })),这很影响性能。现在字符串插值被改进了:
var x = 1; Console.WriteLine($"hello, {x}");会被编译成:
int x = 1; DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(7, 1); defaultInterpolatedStringHandler.AppendLiteral("hello, "); defaultInterpolatedStringHandler.AppendFormatted(x); Console.WriteLine(defaultInterpolatedStringHandler.ToStringAndClear());上面这个 DefaultInterpolatedStringHandler 也可以借助 InterpolatedStringHandler 这个 attribute 替换成你自己实现的插值处理器,来决定要怎么进行插值。借助这些可以实现接近零开销的字符串插值。
Source Generator v2代码生成器在 C# 10 将会迎来 v2 版本,这个版本包含很多改进,包括强类型的代码构建器,以及增量编译的支持等等。