C#中绑定值类型的泛型参数如何声明其Nullable可空版本
C# 泛型Result类的值类型可空适配问题
问题描述
我编写了一个用于表示执行成功结果或错误信息(二者互斥,不会同时存在)的泛型类,实现代码如下:
public class Result<TValue, TError> where TValue : notnull where TError : notnull { private readonly bool succeeded; private readonly TValue? value; private readonly TError? error; public static Result<TValue, TError> Success(TValue value) => new(true, value ?? throw new ArgumentNullException(nameof(value)), default); public static Result<TValue, TError> Failure(TError error) => new(false, default, error ?? throw new ArgumentNullException(nameof(error))); protected Result(bool succeeded, TValue? value, TError? error) { this.succeeded = succeeded; this.value = value; this.error = error; } public bool Successful( [NotNullWhen(true)] out TValue? value, [NotNullWhen(false)] out TError? error) { if (succeeded) { value = this.value!; error = default; return true; } else { value = default; error = this.error!; return false; } } }
该实现的实际表现如下:
- 对引用类型可符合预期工作:例如当
TValue为object类型时,value字段与对应入参的类型均为object? - 当泛型类型参数为值类型时,为其添加
?修饰符并不会使其变为Nullable<T>类型,本质是该操作对值类型属于实际类型变更,而非仅添加可空性标注
核心疑问:是否存在可行方法,能够引用泛型类型对应的
Nullable版本?
解答
C#泛型的?标注行为遵循以下规则:
- 泛型参数无
class/struct显式约束时,T?属于编译期元数据标注:对引用类型会生成可空引用类型标记,对值类型不会自动转为Nullable<T>。本质原因是值类型的可空包装是实际的类型变更,和引用类型仅做元数据标记的可空实现逻辑完全不同。
你当前的实现已经可以满足编译期静态空检查要求:[NotNullWhen]特性已经能向编译器传递准确的空状态信息——Successful返回true时value非空,返回false时error非空,值类型场景下的空引用警告不会误报。
如果需要值类型场景下字段运行时也能正确持有空值,不需要额外实现泛型Nullable版本的引用逻辑。C# 9及以上版本的编译器已经自动处理了这个适配:无约束泛型上的T?会自动适配类型,引用类型生成可空引用标注,值类型自动编译为Nullable<T>。
如果使用旧版本编译器,你现有的实现逻辑也没有运行时问题:你不需要依赖字段的可空类型判断结果状态,因为独立的succeeded字段已经明确标记了成功/失败状态,value和error字段只是附属存储,只需要保留[NotNullWhen]特性即可保证静态检查的正确性。
内容的提问来源于stack exchange,提问作者Giles
相关产品推荐
相关产品推荐

