c# - 避免对具有可为空类型/字段的泛型发出警告的最佳方法?
问题描述
考虑以下人为的类:
public sealed class Test<T>
{
public void SetItem(T item)
{
_item = item;
}
public T GetItem()
{
return _item;
}
T _item; // warning CS8618: Non-nullable field '_item' is uninitialized.
} // Consider declaring the field as nullable.
假设它必须使用以下代码:
var test1 = new Test<int>();
test1.SetItem(1);
Console.WriteLine(test1.GetItem());
var test2 = new Test<string>();
test2.SetItem("One");
Console.WriteLine(test2.GetItem());
进一步假设nullable
在编译时启用。
这个实现会产生一个编译器警告(如果像我们一样,你启用了“作为错误的警告”,它就会变成一个错误):
警告 CS8618:不可为空的字段“_item”未初始化。考虑将该字段声明为可为空。
我的问题很简单:
避免此警告的最佳方法是什么?
我尝试过的事情:
1) 将 _item 声明为可为空:
T? _item; // Error: A nullable type parameter must be known to be a value type or non-nullable reference type
2)将类声明为:
public sealed class Test<T> where T: struct
这避免了警告,但现在该行将var test2 = new Test<string>();
无法编译
3)将类声明为:
public sealed class Test<T> where T: class
...
T? _item;
这也避免了警告,但现在该行将var test1 = new Test<int>();
无法编译。
所以我现在拥有的是:
public sealed class Test<T>
{
public void SetItem(T item)
{
_item = item;
}
public T GetItem()
{
return _item;
}
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
T _item;
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
}
这真的是我最好的选择吗?
注意:我已经看到了这个相关的问题,但它没有回答我的问题,因为答案说使用where T : struct
,这 - 如上所述 - 不能解决问题。
[收到正确答案后编辑]
所以事实证明,对于泛型类型,没有办法使用 nullable 来表达“如果 T 是可空类型,那么它可以为空”的情况。
但是,Resharper 确实允许您这样做,所以我将两者结合使用。
当使用 Resharper 支持以及可为空时,该类看起来像这样:
public sealed class Test<T>
{
public void SetItem([CanBeNull] T item)
{
_item = item;
}
[CanBeNull] public T GetItem()
{
return _item;
}
[CanBeNull] T _item = default!;
}
现在当我写这样的代码时:
var test = new Test<string>();
Console.WriteLine(test.GetItem().Length); // Warning: Possible null reference exception.
Resharper 警告我test.GetItem()
可能会返回 null - 即使编译器没有。
(很遗憾,C# 编译器让我无法做到这一点,但至少我们有 Resharper!)
[进一步考虑后编辑]
如果我只是使用这样的类:
public sealed class Test<T>
{
public void SetItem(T item)
{
_item = item;
}
public T GetItem()
{
return _item;
}
T _item = default!;
}
然后实例化代码有责任在需要时提供正确的可空类型 T ,如下所示:
var test = new Test<string?>();
现在,如果您尝试取消引用可能为空的引用,编译器本身会警告您:
var test = new Test<string?>(); // Note "string?"
Console.WriteLine(test.GetItem().Length); // Warning about test.GetItem() possibly null.
这绝对比我在第二次编辑中使用的 Resharper 注释更可取,因为这实际上是错误的,因为这意味着您不能说这些值不为空。
[希望最终澄清编辑]
除非明确设置,否则不允许_item
访问实际的类,所以它更像是这样:
public sealed class Test<T>
{
public void SetItem(T item)
{
_wasSet = true;
_item = item;
}
public T GetItem()
{
if (!_wasSet)
throw new InvalidOperationException("No item set.");
return _item;
}
T _item = default!;
bool _wasSet;
}
因为真实类的使用方式,要设置的项在对象实例化的时候是不可用的,所以无法在构造函数中设置默认值。
(真正的类是有人写的东西来提供一个队列,您可以在其中访问队列中的第一个和第二个项目而不使它们出队,_item
实际上是用于队列前面的单独值,其余的队列被存储在一个Queue<T>
。这不是我做的方式,真的,但这就是我正在使用的......)
解决方案
作为一种可能的选择,您可以结合使用default
文字和null-forgiving
运算符,例如
private T _item = default!;
推荐阅读
- ios - AVAudioEngine - 更改 inputNode 的音频单元设备后输出格式有 0 个通道
- matlab - 如何检查系统是否有 `Simulink Coder 的许可证?
- javascript - 如何在 React 中管理组件状态(加载、错误、数据)
- vips - 带有白色背景的 libvips 中的范围图像
- omnet++ - 一个关于omnet++无线节点源码的问题
- python - 删除没有生命周期规则的旧 s3 对象
- fastapi - Fastapi auth with OAuth2PasswordBearer,如何检查用户是否已连接而不引发异常?
- azure-devops - Azure Dev Ops 登录以访问单个项目
- python - 扩展字典数据框列表
- c# - 如何让 Agora RTM 在 C# web 应用中运行?