我想在C#中使用「is」運算符來檢查對象實例的運行時類型。但它似乎並沒有像我期望的那樣工作。「是」運算符在C#中返回不一致的結果
假設我們有三個組件A1,A2和A3,都只包含一個類。
A1:
public class C1
{
public static void Main()
{
C2 c2 = new C2();
bool res1 = (c2.c3) is C3;
bool res2 = ((object)c2.c3) is C3;
}
}
A2:
public class C2
{
public C3 c3 = new C3();
}
A3:
public class C3
{
}
A1需要引用A2和A3。
A2需要引用A3。
運行後Main()res1和res2設置爲true,如預期。當我開始將A3版本強制命名爲assembly並使A1引用一個版本 和A2引用另一個版本的A3(A3的源代碼保持不變)時,會出現問題。順便說一句。只有當A2引用的A3版本低於或等於A1引用的A3版本 時,編譯器才允許這樣做。這個程序的結果現在是不同的(res1 = true,res2 = false)。
此行爲是否正確?他們不應該都是假的(或者也許是真的)?
根據C#5.0規範(章節7.10.10),res1和res2應該以相同的值結束。 「is」操作符應始終考慮實例的運行時類型。
在IL代碼中,我可以看到res1編譯器做出了這樣的決定,即來自不同A3程序集的兩個C3類相等,並且發出代碼,而不用isinst指令檢查僅針對null。對於res2,編譯器添加了isinst指令,它推遲了運行時間的決定。 它看起來像C#編譯器有關如何解決此問題比CLR運行時不同的規則。
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 36 (0x24)
.maxstack 2
.locals init ([0] class [A2]C2 c2,
[1] bool res1,
[2] bool res2)
IL_0000: nop
IL_0001: newobj instance void [A2]C2::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldfld class [A3]C3 [A2]C2::c3
IL_000d: ldnull
IL_000e: ceq
IL_0010: ldc.i4.0
IL_0011: ceq
IL_0013: stloc.1
IL_0014: ldloc.0
IL_0015: ldfld class [A3]C3 [A2]C2::c3
IL_001a: isinst [A3_3]C3
IL_001f: ldnull
IL_0020: cgt.un
IL_0022: stloc.2
IL_0023: ret
} // end of method C1::Main
難道只是爲了更快和優化的實現而不使用isinst(考慮到編譯器警告)進行權衡?
解決此問題的可能選項是綁定重定向(如警告中所建議的),但我無法使用該版本,因爲版本可能不總是向後兼容(儘管C3類始終是)。改變A2中的引用對我來說也不是一種選擇。
編輯:因爲它似乎最簡單的解決方法是總是強制轉換爲對象以獲得正確的結果。
無論如何,它仍然很有趣,知道它是否是C#編譯器中的錯誤(並且可能向MS報告)或者本身沒有錯誤(因爲編譯器識別問題並報告警告),儘管它仍然可以生成正確的IL代碼。
類型* *由於*不同*組件而有所不同。程序集的強名稱包括版本號 - 您明確指定兩個二進制文件不再相同,但您認爲它們不相同,但後者與較早版本兼容,前提是沒有公共類型會破壞兼容性。 C#規則處理語言,而您有兩個不同的IL二進制文件。 – 2015-03-13 10:28:39
是的,這就是我期望的不同組件。那麼爲什麼res1設置爲true呢? – Erik 2015-03-13 11:07:45
什麼是編譯器警告? – 2015-03-13 21:33:24