Neden C # derleyicisi bunu bir> karşılaştırma gibi çevirir! = Karşılaştırması?


147

C # derleyicisinin bu yöntemi değiştirdiğini tamamen tesadüfen keşfettim:

static bool IsNotNull(object obj)
{
    return obj != null;
}

… Bu CIL'de :

.method private hidebysig static bool IsNotNull(object obj) cil managed
{
    ldarg.0   // obj
    ldnull
    cgt.un
    ret
}

… Veya derlenmiş C # koduna bakmayı tercih ederseniz:

static bool IsNotNull(object obj)
{
    return obj > null;   // (note: this is not a valid C# expression)
}

Nasıl !=oluyor da " >" olarak çevriliyor ?

Yanıtlar:


201

Kısa cevap:

IL'de "eşit olmayan karşılaştırma" talimatı yoktur, bu nedenle C # !=operatörünün tam bir karşılığı yoktur ve harfi harfine tercüme edilemez.

Bununla birlikte, "karşılaştırmalı" bir talimat vardır ( ceq, ==operatöre doğrudan bir karşılık gelir ), bu nedenle genel durumda, x != ybiraz daha uzun olan eşdeğeri gibi çevrilir (x == y) == false.

Orada da bir "karşılaştırma-büyüktür" IL (öğretim cgt) derleyici boş karşı nesnelerin o eşitsizlik karşılaştırmaları olmak (yani daha kısa IL kodu oluşturmak) birini belli kısayollar almasına izin veriyor, obj != nullonlar sanki, "tercüme olsun obj > null".

Biraz daha ayrıntıya girelim.

IL'de "eşit olmayan karşılaştırma" talimatı yoksa, aşağıdaki yöntem derleyici tarafından nasıl çevrilecek?

static bool IsNotEqual(int x, int y)
{
    return x != y;
}

Yukarıda da belirtildiği gibi, derleyici şuna x != ydönüşecektir (x == y) == false:

.method private hidebysig static bool IsNotEqual(int32 x, int32 y) cil managed 
{
    ldarg.0   // x
    ldarg.1   // y
    ceq
    ldc.i4.0  // false
    ceq       // (note: two comparisons in total)
    ret
}

Derleyicinin her zaman bu oldukça uzun soluklu modeli üretmediği ortaya çıktı. y0 sabitiyle değiştirdiğimizde ne olacağını görelim :

static bool IsNotZero(int x)
{
    return x != 0;
}

Üretilen IL, genel durumdakinden biraz daha kısadır:

.method private hidebysig static bool IsNotZero(int32 x) cil managed 
{
    ldarg.0    // x
    ldc.i4.0   // 0
    cgt.un     // (note: just one comparison)
    ret
}

Derleyici imzalı tamsayı saklanır gerçeği yararlanabilir iki tamlayıcısı (- yani en neyi elde edilen bit desenleri işaretsiz tamsayı olarak yorumlanır eğer nerede, .unaraç - 0 Mümkün olan en küçük değere sahip), bu çevirir böylece x == 0o sanki unchecked((uint)x) > 0.

Derleyicinin aşağıdakilere karşı eşitsizlik kontrolleri için aynısını yapabileceği ortaya çıktı null:

static bool IsNotNull(object obj)
{
    return obj != null;
}

Derleyici, aşağıdakilerle neredeyse aynı IL'yi üretir IsNotZero:

.method private hidebysig static bool IsNotNull(object obj) cil managed 
{
    ldarg.0
    ldnull   // (note: this is the only difference)
    cgt.un
    ret
}

Görünüşe göre, derleyicinin nullreferansın bit modelinin herhangi bir nesne referansı için mümkün olan en küçük bit modeli olduğunu varsaymasına izin verilmektedir .

Bu kısayol, Ortak Dil Altyapısı Açıklamalı Standartta (Ekim 2003'ten itibaren 1. baskı) (sayfa 491, Tablo 6-4, "İkili Karşılaştırmalar veya Dal İşlemleri" dipnotu olarak ) açıkça belirtilmiştir :

" cgt.unObjectRefs (O) üzerinde izin verilir ve doğrulanabilir. Bu, genellikle bir ObjectRef'i null ile karşılaştırırken kullanılır (aksi takdirde daha açık bir çözüm olacak" eşit olmayan karşılaştırma "talimatı yoktur)."


3
Mükemmel cevap, sadece bir nit: ikinin tamamlayıcısı burada geçerli değildir. Sadece imzalı tamsayı olmayan negatif değerler şekilde saklanır bu konularda int'nin aralığında aynı gösterimi intonlar olduğu gibi uint. Bu ikinin tamamlayıcısından çok daha zayıf bir gereksinimdir.

3
İmzasız türlerde hiçbir zaman negatif sayı olmaz, bu nedenle sıfırla karşılaştıran bir karşılaştırma işlemi sıfır olmayan herhangi bir sayıyı sıfırdan küçük olarak değerlendiremez. Olmayan negatif değerlere karşılık gelen tüm gösterimleri intzaten aynı değerde tarafından atılmıştır uintolumsuz değerlerine karşılık gelen tüm gösterimleri, böylece inttekabül eder zorunda bazı değerinden uintbüyükse 0x7FFFFFFF, ancak hangi değerin maddenin gerçekten değil dır-dir. (Aslında, gerçekten gerekli olan tek şey sıfırın hem intve hem de içinde aynı şekilde temsil edilmesidir uint.)

3
@hvd: Açıkladığınız için teşekkürler. Haklısın, önemli olan ikinin tamamlayıcısı değil; o gerekliliktir Bahsettiğiniz o ve aslında o cgt.unmuamele eder intbir şekilde uintyatan bit deseni değiştirmeden. (Düşünün cgt.unöncelikle besbelli yerine olamayacağını durumda 0'a tüm negatif sayılar eşleyerek düzeltme underflows çalışacağını > 0için != 0.)
stakx - artık katkıda

2
Bir nesne referansını kullanarak başka bir nesne referansını karşılaştırmanın >doğrulanabilir IL olması şaşırtıcı buluyorum . Bu şekilde, boş olmayan iki nesneyi karşılaştırabilir ve bir mantıksal sonuç elde edebilir (bu, deterministik değildir). Bu bir bellek güvenliği sorunu değil, ancak genel olarak güvenli yönetilen kod ruhuna uygun olmayan kirli bir tasarım gibi geliyor. Bu tasarım, nesne referanslarının işaretçiler olarak uygulandığı gerçeğini sızdırıyor. .NET CLI'nin tasarım hatası gibi görünüyor.
usr

3
@usr: Kesinlikle! CLI standardının III.1.1.4 Bölümü , "Nesne referanslarının (O tipi) tamamen opak olduğunu" ve "izin verilen tek karşılaştırma işlemlerinin eşitlik ve eşitsizlik ..." olduğunu söyler . Nesne referanslar vardır Belki de değil bellek adresleri cinsinden tanımlanmaktadır, standart, kavramsal olarak (örneğin tanımları 0 ayrı sıfır referansı tutmak ilgilenir ldnull, initobjve newobj). Dolayısıyla, cgt.unnesne referanslarını boş referansla karşılaştırmak için kullanılması, bölüm III.1.1.4 ile birden fazla şekilde çelişiyor gibi görünmektedir.
stakx -
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.