资讯动态

NET 11 Preview 5:C# 15 里更完整的 union、closed 和 unsafe 体验

发布时间:2026/8/7 12:31:04 来源:尧图企业网站定制
.NET 11 Preview 5C# 15 里更完整的 union、closed 和 unsafe 体验.NET 11 Preview 5 带来了一组 C# 语言和编译器更新。它们看起来分散一个是union一个是closed类层级还有一个是unsafe代码边界的变化。这篇文章结合一个简单 Demo看看这几个更新分别解决什么问题。注意本文内容基于 .NET 11 Preview 5相关语法仍处在预览阶段。项目中需要开启LangVersionpreview部分特性在当前预览版里还需要手动提供 compiler support attribute。项目准备示例项目使用net11.0并开启 C# 预览语法ProjectSdkMicrosoft.NET.SdkPropertyGroupOutputTypeExe/OutputTypeTargetFrameworknet11.0/TargetFrameworkImplicitUsingsenable/ImplicitUsingsNullableenable/NullableLangVersionpreview/LangVersionAllowUnsafeBlockstrue/AllowUnsafeBlocks/PropertyGroup/Project在当前 Preview 中如果要声明union或closed还需要在编译单元里提供对应的支持类型namespaceSystem.Runtime.CompilerServices;[AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct,AllowMultiplefalse)]publicsealedclassUnionAttribute:Attribute{}publicinterfaceIUnion{object?Value{get;}}[AttributeUsage(AttributeTargets.Class,AllowMultiplefalse,Inheritedfalse)]publicsealedclassClosedAttribute:Attribute{}union把“几种可能结果”写进类型系统先看union。假设我们有两种宠物类型publicrecordclassDog(stringName);publicrecordclassCat(intLives,intAge);现在可以声明一个PetpublicunionPet(Dog,Cat);这表示Pet的值只能是Dog或Cat。它不是一个随便装什么都行的object也不是靠约定维护的包装类而是一个有明确 case type 的联合类型。使用时Dog和Cat可以隐式转换成PetPetpet1newDog(Lucky);Petpet2newCat(9,3);然后可以直接对Pet做模式匹配staticstringGetPetInfo(Petpet)petswitch{Dog(stringname)$Dog named{name},Cat(intlives,_)$Cat with{lives}lives,};这里有两个点值得注意。第一Dog(string name)和Cat(int lives, _)是位置模式。它会匹配具体类型并把 record 主构造函数里的字段解构出来。第二Cat(int lives, _)里的_是弃元模式表示第二个字段我不关心。它不是“详尽模式”。真正的“详尽”叫穷尽性检查也就是编译器判断这个switch是否覆盖了所有可能情况。因为Pet只有Dog和Cat两种 case type所以当我们把这两个分支都写出来时switch不需要再额外写_...如果给Pet再加一个 case type但是忘记更新switch编译器就可以提醒我们这个匹配没有覆盖完整。这就是union的价值把“这个值只可能是 A 或 B”从口头约定变成类型系统的一部分。closed给继承体系补上穷尽性检查closed和union很像它们都能帮助编译器做穷尽性检查。但两者的建模方式不同。union更像是组合publicunionPet(Dog,Cat);而closed面向的是继承体系。比如门的状态namespaceSomeClass;publicclosedrecordGateState;publicrecordClosed:GateState;publicrecordOpen(floatPercent):GateState;closed的意思是GateState只能在同一个程序集中被直接继承。因此编译器可以知道GateState的直接派生类型有哪些。于是这个switch就可以被判断为完整staticstringGetGateStatus(GateStatestate)stateswitch{ClosedThe gate is closed.,Open(floatpercent)$The gate is{percent*100}% open.};如果GateState没有closed那么外部程序集理论上可以继续声明新的派生类型publicrecordLocked:GateState;这时编译器就无法确认GetGateStatus是否已经覆盖了所有状态。加上closed之后外部程序集不能直接继承GateStatepublicrecordLocked:GateState;// 不允许GateState 是 closed这就是closed能支持穷尽性检查的原因。closed 不等于 sealed这里非常容易误解closed不是sealed。closed限制的是“谁能直接继承这个基类”。它不表示整棵继承树都被封死。比如在类库里publicclosedrecordGateState;publicrecordClosed:GateState;publicrecordOpen(floatPercent):GateState;外部程序集不能这样写publicrecordAAA:GateState;// 不可以但如果Closed本身是 public并且没有sealed或closed外部程序集可以继续继承ClosedpublicrecordAAA:Closed;// 可以这不会破坏GateState上的穷尽性检查因为AAA仍然属于Closed这个分支GetGateStatus(newAAA());上面的调用会匹配到ClosedThe gate is closed.所以可以这样区分closed限制直接派生类型必须声明在同一程序集主要服务于模式匹配的穷尽性检查。sealed禁止继续继承这个类型。如果你想让Closed不能再被继承那仍然应该写publicsealedrecordClosed:GateState;另外友元程序集也不是“同一程序集”。InternalsVisibleTo能让另一个程序集访问internal成员但不能把它变成closed规则里的 same assembly。union 和 closed 到底有什么区别它们确实很像尤其是从模式匹配的角度看二者都在表达“可能性是有限的”。区别主要在建模方向。union是从一个值的位置出发publicunionPet(Dog,Cat);它表达的是这个Pet值只能是Dog或Cat。closed是从一组类型家族出发publicclosedrecordGateState;publicrecordClosed:GateState;publicrecordOpen(floatPercent):GateState;它表达的是GateState这个基类的直接派生类型只有这些。如果几个类型本来就属于同一个继承体系比如订单状态、支付状态、流程节点closed会很自然。如果几个类型不一定应该共享一个基类只是某个返回值可能是其中一种比如成功、失败、校验错误union会更合适。unsafe把不安全边界放到真正危险的地方Preview 5 还有一个和unsafe有关的变化指针类型和指针值可以出现在unsafe上下文之外。以前我们看到指针通常就要把一大段代码包进unsafe。现在边界更精确了声明指针、拿到指针值本身不一定需要unsafe真正需要unsafe的是解引用指针或者调用被标记为unsafe的成员。示例intvalue42;int*pointervalue;这段代码可以出现在普通代码上下文里。因为到这里为止我们只是拿到了一个指针值还没有通过它读取或写入内存。真正危险的是下面这一步unsafe{Console.WriteLine(*pointer);}*pointer是指针解引用程序开始访问指针指向的内存因此它仍然必须出现在unsafe上下文中。这个变化不是让 C# 的不安全代码变得更随意反而是让unsafe的边界更准确。以前unsafe很容易覆盖一整个方法导致真正需要审查的地方被淹没。现在我们可以把unsafe缩小到真正发生不安全操作的位置解引用指针、写入非托管内存、调用unsafe成员。这对代码审查和大型项目维护很有帮助。看到unsafe的时候我们更容易判断这里确实发生了需要人工确认的内存操作。小结这个变化不是让 C#的不安全代码变得更随意反而是让 unsafe 的边界更准确。以前 unsafe 很容易覆盖一整个方法导致真正需要审查的地方被淹没。现在我们可以把 unsafe 缩小到真正发生不安全操作的位置解引用指针、写入非托管内存、调用 unsafe 成员。这对代码审查和大型项目维护很有帮助。看到 unsafe 的时候我们更容易判断这里确实发生了需要人工确认的内存操作。.NET11也一如既往的 针对语法性能进行了不小的优化请继续期待未来的更新视频点击下方查看原文Bilibili: 无聊的年微信公众号: scixing的炼丹房参考.NET 11 Preview 5 C# Release Notes

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价