簡體   English   中英

在某些但不是所有參數情況下意外需要斷言 Collection?.Method(...)

[英]Unexpected need to assert Collection?.Method(...) in some but not all cases of parameters

注意。 問題在於 null 參考保護器運算符 (X?.Y) 的含義。 我知道標題有點神秘,但我還沒有找到更好的方法來表述它。 對不起。

我正在使用FindOneAndReplaceAsync(...) ,根據它,選項的默認值為null

  await Things.FindOneAndReplaceAsync(
    x => x.Id == target.Id, replacement, null, token);

傳遞一個明確的null就像預期的那樣工作。 但是,當我創建FindOneAndReplaceOptions的實例時,編譯器突然擔心 object 我正在執行未定義的搜索。

FindOneAndReplaceOptions<Thing> options = new FindOneAndReplaceOptions<Thing>();
await Things.FindOneAndReplaceAsync(
  x => x.Id == target.Id, replacement, options, token);

為了編譯,我必須斷言Things為非空。

FindOneAndReplaceOptions<Thing> options = new FindOneAndReplaceOptions<Thing>();
await Things?.FindOneAndReplaceAsync(
  x => x.Id == target.Id, replacement, options, token);

雖然我理解這個建議,但我無法理解為什么只有在通過選項 object 的實例時才會出現投訴。 我檢查了我沒有因超載而絆倒。 即使沒有通過實際實例,也會出現同樣的現象,就像這樣。

FindOneAndReplaceOptions<Thing> options = null;
await Things?.FindOneAndReplaceAsync(
  x => x.Id == target.Id, replacement, options, 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上查看。

這實際上歸結為三件事:

  1. 重載解析更喜歡實例方法而不是擴展方法。
  2. 您可以在null接收器上調用擴展方法,但不能調用實例方法。
  3. MongoDB 沒有可空性注釋

請注意, IMongoCollection<TDocument>.FindOneAndReplaceAsync<TProjection>(...)的簽名(這是此 MongoDB 方法的簡化版本)具有TProjection類型參數,該參數鏈接到FindOneAndReplaceOptions<TDocument, TProjection>參數。

如果您傳入null作為options的值,編譯器無法推斷TProjection類型參數是什么,因此重載解析失敗,它會轉移到擴展方法上。

FindOneAndReplaceAsync擴展方法(這是此 MongoDB 方法的簡化版本)沒有TProjection類型參數,其options參數的類型為FindOneAndReplaceOptions<TDocument, TDocument> )。

這意味着編譯器具有推斷所有類型參數所需的所有信息,即使您將null作為options的值傳遞也是如此。

因此,當您調用collection.FindOneAndReplaceAsync(null)時,編譯器無法綁定到實例方法(因為它無法推斷TProjection ),並且必須綁定到擴展方法。

由於 MongoDB 沒有可空性注釋,編譯器根本不知道doc參數的null值是否被允許,因此它假定它是允許的。 因此,沒有可空性警告。

但是,當您傳入FindOneAndReplaceOptions<TDocument>的實例(從FindOneAndReplaceOptions<TDocument, TDocument>繼承時,編譯器有足夠的信息來推斷TProjection的類型:它與TDocument相同,因此它現在可以綁定到實例方法,並在可能是null的東西上調用實例方法值得警告。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM