C# 8中的可空引用類型


原文:Nullable Reference Types In C# 8
作者:.NET Core Tutorials
譯者:Lamond Lu

現狀

可空引用類型

自從我開始學習.NET, 引用類型一直就是可空的。然而初級程序員通常會告訴你值類型不可空,引用類型可空。

事實上,在.NET中有一種語法可以表明一個值類型是否可空。

int? nullableInt1 = null;
Nullable<int> nullableInt2 = null;
int nullableInt3 = null; //編譯錯誤

並且這種語法並不只適用於原始類型,它也適用於struct

Tips: Struct本身就是值類型

struct MyStruct
{
 
}
        
static void Main(string[] args)
{
    MyStruct? mystruct1 = null;
    MyStruct myStruct2 = null;
}

但是現在我們希望在編譯以下代碼時,編譯器能給出錯誤或者警告

class MyClass
{
 
}
        
static void Main(string[] args)
{
    MyClass myClass = null; 
}

為什么?

這里我們第一個問題就是,為什么需要讓編譯器給出錯誤或者警告?

我們接下來已一段簡單的代碼為例。

class MyClass
{
    public void SayHello()
    {
        Console.WriteLine("Hello");
    }
}
        
static void Main(string[] args)
{
    var myClass = new MyClass(); 
    myClass.SayHello();
}

這個代碼是某個功能的最初版本,看起來非常的簡單,並且會運行的很好。

現在我們想象一下,一段之間之后,另外一個程序員加入了項目,將程序修改如下

class MyClass
{
    public void SayHello()
    {
        Console.WriteLine("Hello");
    }
}
        
static void Main(string[] args)
{
    var myClass = new MyClass();
 
    ...
    
    if (true)  
    {
        myClass = null;
    }
    
    ...
 
    if(myClass == null)
    {
        ...
    }
    
    ...

    myClass.SayHello();
}

這樣的代碼看起來很傻,但是現實情況中確實會發生,有人會將myClass設置為null來滿足他們正在處理的功能。它深藏在程序中,甚至可以通過單元測試,所有的功能看起來都運行良好。

但是在某個特定的時間點, 特定的條件下,程序會拋出一個NullReferenceException空引用異常, 這時候我們才會發現我們缺少了空引用判斷,然后添加一定的防護。

class MyClass
{
    public void SayHello()
    {
        Console.WriteLine("Hello");
    }
}
        
static void Main(string[] args)
{
    var myClass = new MyClass();
 
    ...
    
    if (true)  
    {
        myClass = null;
    }
    
    ...
 
    if(myClass == null)
    {
        ...
    }
    
    ...

    if(myClass != null)
    {
         myClass.SayHello();   
    }
}

那么如何避免其他程序員,或者未來的自己,陷入這種空引用的陷阱呢?

啟用可空引用類型

如上所述,這里我們首先需要使用C#8的Nullable Reference Types功能。 完成后,只需要在項目的csproj文件中添加一行:

<NullableReferenceTypes>true</NullableReferenceTypes>

就可以了。

編譯器產生警告

一旦我們啟用了該功能,讓我們看一段簡單的代碼來說明它是如何工作的。

class MyClass
{
    public void SayHello()
    {
        Console.WriteLine("Hello");
    }
}
 
static void Main(string[] args)
{
    MyClass myClass = null;
    myClass.SayHello();
}

如果編譯以上代碼的話,我們會得到2個警告。這里我使用了加粗字體,是因為我們得到的只是警告,不是編譯錯誤。你的程序依然可以編譯和啟動。

第一個警告是我們嘗試將null分配給未明確設置為允許空值的變量。

Converting null literal or possible null value to non-nullable type.

第二個警告是當我們嘗試實際使用非可空類型時,編譯器認為它將為null。

Possible dereference of a null reference.

所以這兩個警告都不會阻止我們的應用程序運行,但它會警告我們我們可能遇到麻煩。

下面讓我們修改代碼,讓我們的引用類型變量可空

C# 8中可用引用類型的定義於可空值類型一樣,即在聲明時,類型名的后面加?號

static void Main(string[] args)
{
    MyClass? myClass = null;
    myClass.SayHello();
}

這里有趣的是,修改完代碼后,編譯項目,你依然會收到Possible dereference的警告。為了消除掉這個警告,你可以添加空引用檢查。

static void Main(string[] args)
{
    MyClass? myClass = null;
    if (myClass != null)
    {
        myClass.SayHello();
    }
}

至此,所有的警告都消失了。

編譯器警告的限制

在我們實際編碼過程中,引用類型可以在方法,類,甚至程序集中傳遞。因此拋出警告時,它並不是萬無一失的。例如,我們有如下代碼:

class MyClass
{
    public Random Random = new Random();
}
 
 
static void Main(string[] args)
{
    MyClass myClass = new MyClass();
    SomeMethod(myClass);
    var next = myClass.Random.Next(1, 10);
}
 
static void SomeMethod(MyClass myClass)
{
    myClass.Random = null;
}

這里編譯器只會警告我們在分配一個null值給一個沒有明確指定可空的變量。但是我們不會得到Possible dereference的警告。這里我們可以推斷,一旦將對象傳遞到方法之外,無論在那里發生什么(如設置null),我們都不會被警告。但是如果我們在相同的代碼/方法塊中如此明確地分配null,然后嘗試使用它,那么編譯器將嘗試給我們一個幫助。

為了與上述代碼比較,以下代碼確實會收到2條警告


static void Main(string[] args)
{
    MyClass myClass = new MyClass();
    if (new Random().Next(1, 10) > 5)
    {
        myClass = null;
    }
 
    myClass.SayHello();
}

啟用可空引用類型的嚴格模式

如果你希望用錯誤替換警告,你可以升級整個檢查到嚴格模式。這里你只需要在項目的csproj文件中添加一行:

<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

注意: 這會將所有警告視為錯誤,而不僅僅是關於空引用問題的警告。但這意味着如果有警告被拋出,你的項目將不再編譯!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM