【C#探索】.NET10 C#14前瞻
一年一度的.NET Conf 2025,即将11月12日0点正式开幕,.NET10第一个正式版也将随即发布。今天我们就来介绍一下, .NET10与C#14,有哪些比较可能常用的特性吧。
Tagged Union
可区分联合再一次的咕咕咕了,可能要在未来的版本中我们才能正式见识到这个强大的类型,不过我们目前依然可以用接口集成,来简单的模拟这个类型(抽象类型也同理)。
public interface IBattleStatus;
public record PlayerStatus(int level) : IBattleStatus;
public record EmptyStatus() : IBattleStatus;
Extension新语法
在C#14中,扩展方法迎来了全新的语法,之前版本中使用的预览的语法也已彻底废弃。
这也是我认为最重要的语法更新。
原有方式
public static BattleData GetNewTurn1(this BattleData battleData1)
{
var newTurn = battleData1.Turn + 1;
var newBattleTurns = battleData1.BattleTurns.Add(new BattleTurn());
return battleData1 with { Turn = newTurn, BattleTurns = newBattleTurns };
}
现有方式
public record BattleData(
int Turn,
ImmutableArray<BattleTurn> BattleTurns,
IBattleStatus BattleStatus
);
public static class SomeExtension
{
extension(BattleData battleData)
{
public BattleData GetNewTurn()
{
var newTurn = battleData.Turn + 1;
var newBattleTurns = battleData.BattleTurns.Add(new BattleTurn());
return battleData with { Turn = newTurn, BattleTurns = newBattleTurns };
}
public bool IsPlayerTurn()
{
return battleData.BattleStatus is PlayerStatus;
}
public BattleData ChangeStatus (IBattleStatus newStatus)
{
return battleData with { BattleStatus = newStatus };
}
}
}
同时扩展方法也支持定义除了方法外的,也支持了属性,和运算符的定义。借由此更新,我们不仅能更轻松的编写大量的扩展方法,也可以激发出C#更强大的潜力
在编写函数式的代码的时候,我们往往会用到大量的扩展方法,新语法的引入让我们代码的噪音变得更少,整体上也看起来更简洁。
配合上record之后,我们就可以完成一道不可变的链式调用。
var battleData = new BattleData(
Turn: 0,
BattleTurns: ImmutableArray<BattleTurn>.Empty,
BattleStatus: new PlayerStatus(level: 1)
);
battleData = battleData
.GetNewTurn()
.ChangeStatus( new PlayerStatus(level: 2))
;
Console.WriteLine(battleData);
当然,这并不是函数式编程的全部,如果对函数式编程编程有所兴趣,欢迎观看函数式编程系列。
这里还是再次强调一件事,编程语言能不能做一件事,和方不方便做一件事是不一样的。如果编程语言越期待用户能以某种形式去做到某件事,这件事也应该更容易,更简洁的去被做到。而不断优化的扩展方法,将会让C#更加适合的去编写函数式的代码。尽管C#不会成为纯粹的函数式编程语言,但是其正在不断的吸收函数式编程中优秀的特性,这一定会是未来的趋势。
另外,在C#14中引入了对运算符的扩展方法,我们甚至可以做到一下这件事
_ = "Hello, World!"
| Console.WriteLine;
public static class C
{
extension<T>(T)
{
public static T operator |(T source, Action<T> action)
{
action(source);
return source;
}
}
}
就像管道一样!尽管我们只能用现有的运算符去模拟这件事,但这本身就足以令人震惊。若是未来开放了自定义的运算符,我们可能就能真正意义上的颠覆了C#原有的代码风格
field(幕后变量)
终于在C#14中,幕后变量被引入了我们的代码中,我们现在可以不再需要总是编写属性的完整代码,编译器将会自动引入幕后变量帮我们完成这件事
其中MyProperty采用了原有的完整代码,而FieldProperty则是采用最新的幕后变量
可以看到,我们可以不再需要去手动定义属性所需要的幕后变量了,节省了不少事。
public class FieldInProperty
{
private int myProperty = 10;
public int MyProperty { get => myProperty; set => myProperty = value; }
public int[]? ArrayProperty { get; set; } = null;
public int FieldProperty { get => field;
set {
field = value;
Console.WriteLine("FieldProperty设置为{0}", value);
}
}
}
Null 条件分配
在这之前,我们可以用?.的方式,来避免Null的方法调用,现在将此特性引入了赋值。
所以我们可以做出以下操作
FieldInProperty? nullableFieldInProperty = null;
nullableFieldInProperty?.FieldProperty = 2;
nullableFieldInProperty?.ArrayProperty?[0] = 1;
现在我们即使对null的变量赋值,也不会有任何问题,这等效于嵌套了一层if判断。
对 JSON 序列化程序的 PipeReader 支持
Json和EFCore事实上有非常多的更新,在本篇简介中只简单介绍。未来将会更细致的介绍
在最新的json库中,对反序列化引入了PipeReader 的支持,也就是我们现在可以做如下操作
var pipe = new Pipe();
// Serialize to writer
await JsonSerializer.SerializeAsync(pipe.Writer, new Person("Alice"));
await JsonSerializer.SerializeAsync(pipe.Writer, new Person("Alice1"));
await JsonSerializer.SerializeAsync(pipe.Writer, new Person("Alice2"));
await JsonSerializer.SerializeAsync(pipe.Writer, new Person("Alice3"));
await pipe.Writer.CompleteAsync();
await foreach (var p in JsonSerializer.DeserializeAsyncEnumerable<Person>(pipe.Reader, topLevelValues: true))
{
Console.WriteLine(p.Name);
}
省去了AsSteam, 我们可以直接输入Pipe.Reader 来解析管道中的json数据,如果使用了DeserializeAsyncEnumerable,我们也可以连续的解析管道中的json, 非常方便
带修饰符的简单 lambda 参数
现在无需显示声明out int, 即可直接使用out来定义lambda,省略了一些噪音。
TryParse<int> parse1 = (text, out result) => Int32.TryParse(text, out result);
delegate bool TryParse<T>(string text, out T result);
用户自定义复合赋值 & 部分构造函数
现在我们可以自定义例如+=等符合赋值运算符的行为,已经构造函数也可以使用分部的特性,这对源生存器,将释放更大的能力。
partial class C1
{
public partial C1(int v);
public int Value;
public void operator +=(int x)
{
Value -= x;
}
}
partial class C1
{
public partial C1(int v)
{
Value = v;
}
}
用于字符串比较的数字排序
现在可以通过配置比较器,让字符串中的数字参与正常的大小排序,而不是字典序排序
例如以下代码将输出
Windows 8
Windows 10
Windows 11
StringComparer numericStringComparer = StringComparer.Create(CultureInfo.CurrentCulture, CompareOptions.NumericOrdering);
foreach (string os in new[] { "Windows 8", "Windows 10", "Windows 11" }.Order(numericStringComparer))
{
Console.WriteLine(os);
}
Websocket的抽象API
现在可以通过WebSocketStream.Create创建websocker的Stream,采用更抽象的api管理websocket消息接发
ClientWebSocket connectedWebSocket = new ClientWebSocket();
using Stream transportStream = WebSocketStream.Create(
connectedWebSocket,
WebSocketMessageType.Text,
ownsWebSocket: true);
// Integration with Stream-based APIs.
// Don't close the stream, as it's also used for writing.
using var transportReader = new StreamReader(transportStream, leaveOpen: true);
var line = await transportReader.ReadLineAsync(CancellationToken.None); // Automatic UTF-8 and new line handling.
transportStream.Dispose(); // and more
虽然发出来了晚了一些,不过依然简单总结了一下新的内容~
微信公众号: @scixing的炼丹房
Bilibili: @无聊的年

更多推荐



所有评论(0)