C#编译器bug问询:Nullable<decimal>为何可赋值给仅支持decimal转换的类
问题结论
这不是C#编译器的bug,是符合C#语言规范的合法行为,你遇到的是可空用户自定义转换的自动提升规则的作用。
规则说明
C#规范明确定义了用户自定义转换的提升逻辑:当存在从非可空值类型S到类型T的隐式用户自定义转换时,编译器会自动生成对应的提升转换,支持从S?(可空值类型)到T的隐式转换,转换规则如下:
- 若源
S?实例为null,直接返回T类型的默认值(引用类型默认值为null,值类型为对应默认值),不会触发你定义的转换逻辑 - 若源
S?实例有值,先取出内部的S类型值,执行你定义的用户自定义转换得到T类型实例,作为转换结果返回
对应你的代码场景
你定义了从decimal(非可空值类型S)到DecimalWrapper(引用类型T)的隐式转换,编译器会自动生成从decimal?到DecimalWrapper的隐式提升转换。你传入(decimal?)null时,匹配到这个提升转换,直接返回DecimalWrapper的默认值null,因此编译合法,运行结果也符合规则预期。
你可以通过传入带值的decimal?验证规则:DecimalWrapper c = (decimal?)1.23m;同样可以正常编译,运行时会触发你定义的转换逻辑得到DecimalWrapper实例。
禁用该行为的方案
如果你不允许null的隐式转换,可以通过显式定义decimal?的转换操作符覆盖编译器的自动提升逻辑,示例如下:
public class DecimalWrapper { public static implicit operator DecimalWrapper(decimal x) => new(); // 显式定义decimal?的转换,覆盖自动提升逻辑 public static implicit operator DecimalWrapper(decimal? x) { // 此处可以按你的需求加逻辑,比如禁止null转换直接抛异常 if (!x.HasValue) throw new System.ArgumentNullException(nameof(x), "不允许null转换为DecimalWrapper"); return new DecimalWrapper(); } }
如果需要直接禁止decimal?的隐式转换,可以将上述操作符改为explicit,或者在启用C# 8+可空引用类型的前提下,将DecimalWrapper标记为非可空类型,编译器会对null赋值的行为抛出警告,你可以将对应警告等级调整为错误即可阻止编译。
内容的提问来源于stack exchange,提问作者videokojot
相关产品推荐
相关产品推荐

