為什么強制轉換不適用于約束泛型型別,如下所示?
class B { }
class B1 : B { }
class G<T> where T : B
{
void x()
{
T b1 = new B1(); // why implicit conversion doesn't compile?
T b2 = (T)new B1(); // why explicit conversion doesn't compile either?
T b3 = new B1() as T; // this works!
}
}
uj5u.com熱心網友回復:
B1 不限于可分配給 T。例如:
void Main()
{
new G<C1>().x();
}
class B { }
class B1 : B { }
class C1 : B { }
class G<T> where T : B
{
public void x()
{
T b3 = new B1() as T;
b3.Dump(); // null, because B1 cannot be converted to C1
}
}
僅僅因為 T 被限制為 B 并不意味著您可以將 B 的任何后代強制轉換為任何可能的 T。
為什么不允許呢?在我看來,這沒有任何意義。如果您想確保 B1 可分配給 T,則不應使用泛型。犯這種錯誤太容易了,如果可能,您應該首先避免強制轉換泛型。它們(主要)是為了使靜態型別更強大,同時保持型別安全(和性能優勢)。
但是,肯定有明顯錯誤的情況不會被抓住,因為
T b2 = (T)new B();
確實編譯,即使它實際上有同樣的問題,你會得到一個運行時錯誤鑄如果T是不是B.
當然,在這種情況下,檢查C# 規范會很有幫助,而且很清楚,它說:
上述規則不允許從不受約束的型別引數直接顯式轉換為非介面型別,這可能令人驚訝。這條規則的原因是為了防止混淆并使此類轉換的語意清晰。
雖然這似乎只對值型別有意義,但這解釋了為什么你可以直接轉換為(T)new B();,為什么你不能這樣做(T)new B1();- 即使兩者都有相同的問題,即 T 不一定是 B。
請記住,C#中的運算子不是虛擬的- 它們取決于運算式的編譯時型別。對于值型別引數,您實際上會為您使用的每個值型別獲得一個變體(即List<long>使用與 不同的代碼List<int>) - 因此您將獲得正確的轉換,例如從 int 轉換為 long 時,您將獲得與 int 具有相同值的 long ,而不是鑄造錯誤。
對于參考型別,這不是真的。在你的情況,你可以有從B中的自定義轉換運算子,以B那實際上是在被呼叫(T) new B()時,但沒有從B1到B鑄造,因為具體化泛型型別的G<B>和G<B1>實際上是一樣的。由于這是一個可以隨時更改的實作細節,您確實希望避免混淆和潛在的行為變化。
uj5u.com熱心網友回復:
T b1 = new B1(); // why implicit conversion doesn't compile?
僅僅因為 T 和 B1 都源自 B 并不意味著 B1 可以賦值給 T。它們都可以賦值給 B,所以
B b1 = new B1(); // should work
.
T b2 = (T)new B1(); // why explicit conversion doesn't compile either?
如上所述,具有公共基類的兩個類不能確保型別兼容性,因此顯式轉換也不起作用
T b3 = new B1() as T; // this works!
它有效,但您可能將 null 分配給 b3,因為 B1 不能轉換為 T。 as 運算子只回傳 null 而不是發出編譯器警告或拋出例外。
uj5u.com熱心網友回復:
答案是,因為不能保證您B1作為泛型引數提供,所以不能用泛型來做到這一點,讓我們探討一下為什么......
給定的
class Animal { }
class Dog : Animal { }
class Cat : Animal { }
class Something<T> where T : Animal
{
public T Animal {get;set;}
void x()
{
Animal = (T)new Cat();
}
}
現在,如果你像這樣使用你的類怎么辦
var dog = new Something<Dog>();
dog.X() // internally you are trying to cast a Cat to a Dog
Dog dog = dog.Animal; // now you just tried to mash a Cat into a dog
Dog.Bark() // what the...
約束是一個最低限度的契約,就是這樣。它們允許您在指定的合約上使用通用引數。
這和你為什么不能做下面的事情完全一樣
Dog dog = new Cat();
即使它們都繼承自Animal,也不意味著它們是相同的......它們內部具有不同的記憶體布局,它們具有不同的方法和屬性,它們不能像這樣靜態型別混合在一起。
簡而言之,您可能需要重新考慮您的問題。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/327301.html
