泛型类型参数后的可空标记(?)在值类型实参场景下失效的原因及修复方法咨询
泛型类型参数后的可空标记(?)在值类型实参场景下失效的原因及修复方法咨询
嗨,这个问题我之前也碰到过,一开始懵了好一会儿,我来给你掰扯清楚~
问题原因
你遇到的核心问题是C#里泛型中T?的含义会根据T的类型(引用/值类型)和泛型约束的不同而变化:
- 当
T是引用类型时,T?就是C# 8.0引入的可空引用类型,本质上还是引用类型,只是编译器会帮你做非空检查,所以你传Test?(可空引用)给Bar(T? _)是完全兼容的,这就是第一个例子能正常工作的原因。 - 当
T是值类型且泛型没有约束时,T?的含义会被编译器解析为非可空的值类型本身——因为非可空值类型本来就不能为null,这里的?相当于被直接忽略了!所以Foo<Test>里的Bar(T? _)其实等价于Bar(Test _),而你传的Test?是Nullable<Test>(一个包裹了Test的结构体),自然无法直接转换成Test,于是就报了CS1503错误。
修复方法
我给你整理了几种不同场景下的修复方案,你可以根据自己的需求选:
快速最小改动方案
如果你只是想让当前的值类型场景能跑通,直接在调用Bar的时候传入可空值类型的Value属性就行:foo.Bar(NullableType.Value);不过要注意哦,如果
NullableType是null(也就是HasValue为false),调用Value会抛出InvalidOperationException,所以如果有null的可能,最好先判断HasValue。同时支持引用/值类型的优雅方案
如果你想让Foo<T>的Bar方法同时兼容引用类型的可空引用和值类型的可空值类型,可以给方法做个重载:public class Foo<T> { // 处理非可空T和引用类型的可空引用 public void Bar(T _) { } // 专门处理值类型的可空值类型 public void Bar(Nullable<T> _) where T : struct { Bar(_.Value); // 调用上面的重载,或者在这里直接写业务逻辑 } }这样不管你传的是引用类型的
Test?还是值类型的Test?,都能正确匹配到对应的方法。仅支持值类型的场景
如果你确定Foo<T>只会被值类型使用,那直接给泛型加struct约束就好,这样编译器会把T?正确解析为Nullable<T>:public class Foo<T> where T : struct { public void Bar(T? _) { } }这时候再传
Test?给Bar就完全没问题了。
内容来源于stack exchange
相关产品推荐
相关产品推荐

