为何default(bool?)返回null,泛型default(TValue?)(TValue=bool)却返回false?
问题:default(bool?)返回null,但泛型中default(TValue?)(TValue为bool)返回false?
我们知道default(bool?)会返回null,验证代码如下:
var msg = default(bool?) is null ? "default(bool?) is NULL": "default(bool?) is NOT NULL"; // msg == "default(bool?) is NULL"
但在泛型方法中却出现了不符合预期的结果,泛型方法代码:
static TValue? GetProcItem<TValue>(IDictionary<string, JToken> cache, string key) { if(cache.TryGetValue(key, out var jtoken)) { var value = jtoken.ToObject<TValue>(); return value; } // 当key不存在时返回默认值 TValue? ret = default(TValue?); return ret; }
当指定泛型类型为bool时,default(TValue?)并未按预期返回null:
var cache = new Dictionary<string, JToken>(); // 编译器推断xBool为bool类型而非bool? var xBool = GetProcItem<bool>(cache, "a-key-that-does-not-exist"); // 运行时xBool的值也是bool类型的False Console.WriteLine($"default(bool?)={xBool}; "); //////////// 输出结果:///////////////////////////// //////////// default(bool?)=False;
为什么直接使用default(bool?)返回null,但在泛型中指定TValue=bool时,default(TValue?)却返回false?
原因解析
核心问题出在C#中泛型无约束时T?的语义规则,以及可空类型的语法解析差异:
直接使用
bool?的场景bool?是Nullable<bool>的语法糖,属于显式的可空值类型,default(Nullable<bool>)的默认状态就是null(即HasValue为false的空可空值),这和验证结果完全一致。泛型无约束时
TValue?的解析规则
在泛型方法GetProcItem<TValue>中,没有为TValue添加任何约束(比如where TValue : struct或where TValue : class),此时编译器对TValue?的解析遵循以下规则:
- 如果
TValue是引用类型,TValue?表示可空引用类型(C# 8.0+特性); - 如果
TValue是值类型,编译器无法将TValue?解析为可空引用类型(值类型不具备引用类型的空语义),因此会自动退化为TValue本身——也就是说,此时TValue?等价于TValue,而非Nullable<TValue>。
这就导致:
当调用GetProcItem<bool>时,方法的返回类型TValue?实际等价于bool,而非bool?;default(TValue?)也就等价于default(bool),即false,这就是输出为False的原因。
- 修复方案
如果你希望泛型方法在TValue是值类型时返回Nullable<TValue>,需要为泛型添加struct约束,同时可以拆分方法处理引用类型的情况:
// 针对值类型的版本 static TValue? GetProcItem<TValue>(IDictionary<string, JToken> cache, string key) where TValue : struct { if(cache.TryGetValue(key, out var jtoken)) { return jtoken.ToObject<TValue>(); } return default; // 此时default(TValue?)就是null(Nullable<TValue>的空状态) } // 针对引用类型的版本 static TValue? GetProcItem<TValue>(IDictionary<string, JToken> cache, string key) where TValue : class { if(cache.TryGetValue(key, out var jtoken)) { return jtoken.ToObject<TValue>(); } return default; // 此时default(TValue?)就是null }
这样当调用GetProcItem<bool>时,返回的就是bool?,default会返回null,符合预期。
内容的提问来源于stack exchange,提问作者itminus
相关产品推荐
相关产品推荐

