如何解决在某些但不是所有参数情况下,意外需要断言 Collection?.Method(...)
注意。问题不是关于空引用保护运算符 (X?.Y) 的含义。我知道标题有点神秘,但我还没有想出更好的方法来表达它。对不起。
我正在使用 FindOneAndReplaceAsync(...),根据它,选项的默认值为 null
。
await Things.FindOneAndReplaceAsync(
x => x.Id == target.Id,replacement,null,token);
传递一个明确的 null
就像预期的那样工作。但是,当我创建 FindOneAndReplaceOptions 的实例时,编译器突然担心我正在执行搜索的对象未定义。
FindOneAndReplaceOptions<Thing> options = new FindOneAndReplaceOptions<Thing>();
await Things.FindOneAndReplaceAsync(
x => x.Id == target.Id,options,token);
为了编译,我必须断言 Things
为非空。
FindOneAndReplaceOptions<Thing> options = new FindOneAndReplaceOptions<Thing>();
await Things?.FindOneAndReplaceAsync(
x => x.Id == target.Id,token);
虽然我理解这个建议,但我无法理解为什么只有在传递 options 对象的实例时才会出现投诉。我检查过我没有因超载而绊倒。即使没有实际的实例通过,也会出现同样的现象,就像这样。
FindOneAndReplaceOptions<Thing> options = null;
await Things?.FindOneAndReplaceAsync(
x => x.Id == target.Id,token);
这是怎么回事?感觉很迷茫...
解决方法
这很有趣。如果你把它缩减到最小的复制器,你会得到这个:
#nullable enable
public class C
{
public void M(IMongoCollection<string>? collection)
{
// No warning
collection.FindOneAndReplaceAsync(null);
}
public void N(IMongoCollection<string>? collection)
{
// Warning
collection.FindOneAndReplaceAsync(new FindOneAndReplaceOptions<string>());
}
}
#nullable disable
public interface IMongoCollection<TDocument>
{
Task FindOneAndReplaceAsync<TProjection>(FindOneAndReplaceOptions<TDocument,TProjection> options = null);
}
public static class MongoCollectionExtensions
{
public static Task FindOneAndReplaceAsync<TDocument>(
this IMongoCollection<TDocument> doc,FindOneAndReplaceOptions<TDocument,TDocument> options = null) => Task.CompletedTask;
}
public class FindOneAndReplaceOptions<TDocument,TProjection>
{
}
public class FindOneAndReplaceOptions<TDocument> : FindOneAndReplaceOptions<TDocument,TDocument>
{
}
在 SharpLab 上查看。
这实际上归结为三件事:
- 重载解决方案更喜欢实例方法而不是扩展方法。
- 您可以在
null
接收器上调用扩展方法,但不能调用实例方法。 - MongoDB 没有可空性注释
请注意,IMongoCollection<TDocument>.FindOneAndReplaceAsync<TProjection>(...)
(this MongoDB method 的简化版本)的签名具有 TProjection
类型参数,该参数链接到 FindOneAndReplaceOptions<TDocument,TProjection>
参数。
如果将 null
作为 options
的值传入,编译器无法推断 TProjection
类型参数是什么,因此重载解析失败,并移至扩展方法。
那个 FindOneAndReplaceAsync
扩展方法(它是 this MongoDB method 的简化版本)没有 TProjection
类型参数,它的 options
参数类型为 {{1 }}).
这意味着编译器拥有推断所有类型参数所需的所有信息,即使您将 FindOneAndReplaceOptions<TDocument,TDocument>
作为 null
的值传入。
因此,当您调用 options
时,编译器无法绑定到实例方法(因为它无法推断 collection.FindOneAndReplaceAsync(null)
),而必须绑定到扩展方法。
由于 MongoDB 没有可空性注释,编译器根本不知道 TProjection
参数的 null
值是否被允许,因此它假定它是允许的。因此,没有可空性警告。
然而,当你传入一个 doc
的实例(它继承自 FindOneAndReplaceOptions<TDocument>
时,编译器有足够的信息来推断 FindOneAndReplaceOptions<TDocument,TDocument>
的类型:它与 {{1 }}!所以它现在可以绑定到实例方法,并在可能是 TProjection
的东西上调用实例方法值得警告。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。