避免对可为空 types/fields 的泛型发出警告的最佳方法?
Best way to avoid warnings for generics with nullable types/fields?
考虑以下人为的 class:
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
。
此实现会产生编译器警告(如果您像我们一样启用了 "warnings as errors",则会变成错误):
warning CS8618: Non-nullable field '_item' is uninitialized. Consider declaring the field as nullable.
我的问题很简单:
避免此警告的最佳方法是什么?
我尝试过的事情:
1) 将 _item 声明为可为空:
T? _item; // Error: A nullable type parameter must be known to be a value type or non-nullable reference type
2) 将 class 声明为:
public sealed class Test<T> where T: struct
这避免了警告,但现在行 var test2 = new Test<string>();
不会编译
3) 将 class 声明为:
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.
}
这真的是我最好的选择吗?
注意:我已经看到 this related question,但它没有回答我的问题,因为答案是使用 where T : struct
,如上所述,它不能解决问题。
[收到正确答案后编辑]
所以事实证明,对于泛型类型,没有办法使用 nullable 来表达 "if T is a nullable type, then it can be null".
的情况
但是,Resharper 允许您这样做,所以我结合使用了两者。
class 在使用 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!)
[进一步考虑后编辑]
如果我像这样使用 class:
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 注释更可取,因为这实际上是错误的,因为这意味着您不能说这些值不为空。
[希望最终澄清编辑]
实际的class是不允许访问_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;
}
由于real class的使用方式,在实例化对象的时候,要设置的item是不可用的,所以在构造函数中设置默认是不可能的。
(真正的 class 是有人写的提供队列的东西,你可以在其中访问队列中的第一个和第二个项目而无需将它们出列,而 _item
实际上是一个单独的用于队列前端的值,队列的其余部分存储在 Queue<T>
中。这不是我做的方式,真的,但这就是我正在使用的......)
作为一个可能的选项,您可以使用 default
literal and null-forgiving
运算符的组合,例如
private T _item = default!;
考虑以下人为的 class:
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
。
此实现会产生编译器警告(如果您像我们一样启用了 "warnings as errors",则会变成错误):
warning CS8618: Non-nullable field '_item' is uninitialized. Consider declaring the field as nullable.
我的问题很简单:
避免此警告的最佳方法是什么?
我尝试过的事情:
1) 将 _item 声明为可为空:
T? _item; // Error: A nullable type parameter must be known to be a value type or non-nullable reference type
2) 将 class 声明为:
public sealed class Test<T> where T: struct
这避免了警告,但现在行 var test2 = new Test<string>();
不会编译
3) 将 class 声明为:
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.
}
这真的是我最好的选择吗?
注意:我已经看到 this related question,但它没有回答我的问题,因为答案是使用 where T : struct
,如上所述,它不能解决问题。
[收到正确答案后编辑]
所以事实证明,对于泛型类型,没有办法使用 nullable 来表达 "if T is a nullable type, then it can be null".
的情况但是,Resharper 允许您这样做,所以我结合使用了两者。
class 在使用 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!)
[进一步考虑后编辑]
如果我像这样使用 class:
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 注释更可取,因为这实际上是错误的,因为这意味着您不能说这些值不为空。
[希望最终澄清编辑]
实际的class是不允许访问_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;
}
由于real class的使用方式,在实例化对象的时候,要设置的item是不可用的,所以在构造函数中设置默认是不可能的。
(真正的 class 是有人写的提供队列的东西,你可以在其中访问队列中的第一个和第二个项目而无需将它们出列,而 _item
实际上是一个单独的用于队列前端的值,队列的其余部分存储在 Queue<T>
中。这不是我做的方式,真的,但这就是我正在使用的......)
作为一个可能的选项,您可以使用 default
literal and null-forgiving
运算符的组合,例如
private T _item = default!;