給定以下通用Foo1函式:
struct Key<T> {}。
static readonly Key< double> MyKey = new Key<double>()。
T? Foo1<T>(Key<T> key)
{
return default;
}
一個天真的讀者會認為:
var foo1 = Foo1(MyKey) 。
foo1的型別是double?,事實證明,編譯器正在挑選double作為回傳型別。我需要明確地添加一個約束條件,以獲得一個可歸零的回傳值:
T? Foo2<T>(Key<T>key) where T : struct // really return a nullable。
{
return default;
}
誰能解釋一下,為什么在我的第一個Foo1函式中,nullable參考的注釋沒有被選中?
uj5u.com熱心網友回復:
讓我們先了解一下背景:
在C# 9.0之前Foo1是無效的。即使在C# 8.0中,啟用了可歸零的參考:
CS8627: 一個可置空的型別引數必須知道是一個value型別或者非可置空參考型別
Foo2甚至在C#8.0之前也是有效的,因為T?只有在T是一個結構時才有意義,而在這種情況下,T?有一個與T不同的型別(Nullable<T>)。到目前為止,這是很簡單的。
從C#開始,我們就已經開始使用了。
從C# 8.0開始,引入了nullable參考,這引起了一些混亂。從現在開始,T?既可以表示Nullable<T>,也可以只表示T。這個版本不允許沒有約束條件的T?,但是當你指定where T : class時,它也允許。
如果不使用約束條件,你必須使用屬性來表明T可以是null作為一個回傳值:
// C# 8.0: Poor man's T?
[return: MaybeNull] T Foo1<T> (Key<T> key) => default。
而如果T現在是一個值型別呢?它顯然不會在回傳值中改變其型別為Nullable<T>。要回傳一個double?,你的型別引數也必須是double?,也就是說,MyKey也必須是一個Key<double?>。
在C# 9.0中,對于T?的限制已經放寬,現在它不需要約束:
//C# 9.0:現在有效了。
T? Foo1<T>(Key<T>key) => default。
但它現在的意思基本上與C# 8.0版本相同。如果沒有where T : struct約束,T?與T是同一型別,所以它只不過是表明結果可能是null,這可能會出現在編譯器警告中。要回傳可空值型別,你必須使用double?作為一個通用引數,這也意味著你的Key也必須有一個可空值型別的定義:
static readonly key< double? > MyKey = new Key<double?>()。
如果空鍵在你的情況下沒有意義,那么你除了指定where T : struct約束外,不能做任何事情,就像在Foo2中一樣,所以舊的規則開始生效了。T?和T有不同的型別,其中T?意味著Nullable<T>。
更新: Foo1和Foo2之間的主要區別也許更明顯,如果你看到它們的反編譯源:
[System.Runtime.CompilerServices.NullableContext(2)]
private static T Foo1<T>(【System. Runtime.CompilerServices.Nullable(new byte[ ] {
0,
1.
}) ] Key<T>key)。
{
return default(T)。
}
private static Nullable<T> Foo2<T>(Key<T> key) where T : 結構。
{
return null;
}
注意,Foo1的回傳型別是簡單的T,并帶有一些注釋,因此編譯器可以發出適當的警告。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/318409.html
標籤:
上一篇:為什么在Dart中子型別不能分配給<Textendsbaseclass>泛型?
下一篇:JUnit 通用抽象類測驗基礎:在我的測驗中總是得到nullpointerexception,欄位初始化沒有在抽象方法中完成。
