C # 'da Boks Oluşumu


86

C # 'da boksun meydana geldiği tüm durumları toplamaya çalışıyorum:

  • Değer türünü türe dönüştürme System.Object:

    struct S { }
    object box = new S();
    
  • Değer türünü türe dönüştürme System.ValueType:

    struct S { }
    System.ValueType box = new S();
    
  • Numaralandırma türünün değerini türe dönüştürme System.Enum:

    enum E { A }
    System.Enum box = E.A;
    
  • Değer türünü arayüz referansına dönüştürme:

    interface I { }
    struct S : I { }
    I box = new S();
    
  • C # dize birleştirmede değer türlerini kullanma:

    char c = F();
    string s1 = "char value will box" + c;
    

    not: tür sabitleri charderleme zamanında birleştirilir

    Not: sürüm 6.0 C # derleyicisi beri optimize eder birleştirme içeren bool, char, IntPtr, UIntPtrçeşitleri

  • Değer türü örnek yönteminden temsilci oluşturma:

    struct S { public void M() {} }
    Action box = new S().M;
    
  • Değer türlerinde geçersiz kılınmamış sanal yöntemleri çağırma:

    enum E { A }
    E.A.GetHashCode();
    
  • isİfade altında C # 7.0 sabit kalıplarını kullanma :

    int x = …;
    if (x is 42) { … } // boxes both 'x' and '42'!
    
  • C # tuple türleri dönüşümlerinde boks:

    (int, byte) _tuple;
    
    public (object, object) M() {
      return _tuple; // 2x boxing
    }
    
  • objectDeğer türü varsayılan değerlerine sahip isteğe bağlı türdeki parametreler :

    void M([Optional, DefaultParameterValue(42)] object o);
    M(); // boxing at call-site
    
  • Kısıtlanmamış jenerik türün değeri kontrol ediliyor null:

    bool M<T>(T t) => t != null;
    string M<T>(T t) => t?.ToString(); // ?. checks for null
    M(42);
    

    not: bu, bazı .NET çalışma zamanlarında JIT tarafından optimize edilebilir

  • / Operatörleri structile kısıtsız veya genel tipte tip testi değeri :isas

    bool M<T>(T t) => t is int;
    int? M<T>(T t) => t as int?;
    IEquatable<T> M<T>(T t) => t as IEquatable<T>;
    M(42);
    

    not: bu, bazı .NET çalışma zamanlarında JIT tarafından optimize edilebilir

Bildiğiniz başka gizli, belki de boks durumu var mı?


2
Bununla bir süre önce ilgilenmiştim ve şunu oldukça ilginç buldum: FxCop kullanarak boksu algılama (çözme)
George Duckett

Göstermiş olduğunuz çok güzel dönüşüm yolları.
Aslında ikincisini

12
topluluk wiki sorusu olmalı
Sly

2
Boş değer atanabilir türler ne olacak? private int? nullableInteger
allansson

1
@allansson, null yapılabilir türler sadece değer türleridir
controlflow

Yanıtlar:


42

Bu harika bir soru!

Boks, tam olarak tek bir nedenden dolayı gerçekleşir: bir değer türüne başvurmaya ihtiyacımız olduğunda . Listelediğiniz her şey bu kurala dahildir.

Örneğin, nesne bir başvuru türü olduğundan, nesneye bir değer türü atamak, kutulamaya neden olan bir değer türüne başvuru gerektirir.

Olası her senaryoyu listelemek isterseniz, türevleri de eklemelisiniz, örneğin bu, değer türünü otomatik olarak nesneye / arayüze çevirdiği için, nesne veya arayüz tipi döndüren bir yöntemden bir değer türü döndürmek gibi.

Bu arada, zekice belirlediğiniz dizgi birleştirme durumu da nesneye atamadan kaynaklanıyor. + Operatörü, derleyici tarafından, geçirdiğiniz değer türü için bir nesneyi kabul eden, dizenin Concat yöntemine yapılan bir çağrıya çevrilir, böylece nesneye çevrim ve dolayısıyla kutulama gerçekleşir.

Yıllar boyunca geliştiricilere her vakayı ezberlemek yerine boks yapmanın tek nedenini (yukarıda belirtmiştim) hatırlamalarını tavsiye ettim, çünkü liste uzun ve hatırlaması zor. Bu ayrıca, derleyicinin C # kodumuz için ürettiği IL kodunu anlamayı da destekler (örneğin, dize üzerinde +, String.Concat'e bir çağrı verir). Derleyicinin ne ürettiğinden şüphe duyduğunuzda ve kutulama meydana gelirse, IL Disassembler'ı (ILDASM.exe) kullanabilirsiniz. Tipik olarak, kutu işlem kodunu aramanız gerekir (IL kutu işlem kodunu içermese bile kutunun gerçekleşebileceği tek bir durum vardır, daha fazla ayrıntı aşağıda).

Ama bazı boks olaylarının daha az açık olduğuna katılıyorum. Bunlardan birini listelediniz: bir değer türünün geçersiz kılınmamış bir yöntemini çağırmak. Aslında, başka bir nedenden dolayı bu daha az açıktır: IL kodunu kontrol ettiğinizde, kutu opcode değil kısıtlama opcode görüyorsunuz, bu nedenle IL'de bile kutulamanın gerçekleştiği açık değildir! Bu cevabın neden daha da uzamasını engellemek için tam olarak ayrıntıya girmeyeceğim ...

Daha az belirgin kutulama için başka bir durum, bir yapıdan bir temel sınıf yönteminin çağrılmasıdır. Misal:

struct MyValType
{
    public override string ToString()
    {
        return base.ToString();
    }
}

Burada ToString geçersiz kılınır, bu nedenle MyValType'da ToString'i çağırmak kutulama oluşturmaz. Ancak, uygulama temel ToString'i çağırır ve bu da kutulamaya neden olur (IL'yi kontrol edin!).

Bu arada, bariz olmayan bu iki boks senaryosu da yukarıdaki tek kuraldan kaynaklanıyor. Bir değer türünün temel sınıfında bir yöntem çağrıldığında, this anahtar sözcüğü için başvurulacak bir şey olmalıdır . Bir değer türünün temel sınıfı (her zaman) bir başvuru türü olduğundan, bu anahtar sözcük bir başvuru türüne başvurmalıdır ve bu nedenle, bir değer türüne başvurmaya ihtiyacımız vardır ve bu nedenle tek kural nedeniyle kutulama gerçekleşir.

İşte çevrimiçi .NET kursumun boks yapmanın ayrıntılı olarak tartışıldığı bölümüne doğrudan bir bağlantı: http://motti.me/mq

Yalnızca daha gelişmiş boks senaryolarıyla ilgileniyorsanız, burada doğrudan bir bağlantı var (ancak yukarıdaki bağlantı, daha temel şeyleri tartıştıktan sonra sizi oraya götürecektir): http://motti.me/mu

Umarım bu yardımcı olur!

Motti


1
Eğer a ToString(), onu geçersiz kılmayan belirli bir değer türünde çağrılırsa, değer türü çağrı sitesinde kutuya mı yazılacak, yoksa yöntem, zincirden başka hiçbir şey yapmayan otomatik olarak oluşturulmuş bir geçersiz kılmaya (kutulamadan) gönderilecek mi? boks) temel yönteme?
supercat

@supercat baseBir değer türünde çağıran herhangi bir yöntemi çağırmak kutulamaya neden olur. Bu, struct tarafından geçersiz kılınmayan sanal yöntemleri ve Objecthiç sanal olmayan (benzer GetType()) yöntemleri içerir . Bu soruya bakın .
Şafak Gür

@ ŞafakGür: Sonuçta boks olacağını biliyorum. Bunun gerçekleştiği kesin mekanizmayı merak ediyordum. IL üreten derleyici, türün bir değer türü veya başvuru (genel olabilir) olup olmadığını bilemeyeceğinden bağımsız olarak bir callvirt üretecektir. JITter, türün bir değer türü olup olmadığını ve ToString'i geçersiz kılıp kılmadığını bilir, böylece kutuyu yapmak için çağrı sitesi kodu oluşturabilir; alternatif olarak, geçersiz kılmayan her yapı için otomatik olarak oluşturulabilirToString bir public override void ToString() { return base.ToString(); }
metodu

... boks bu yöntem dahilinde gerçekleşsin. Yöntem çok kısa olacağından, daha sonra sıraya dizilebilir. İkinci yaklaşımla şeyler yapmak, bir yapının ToString()yöntemine tıpkı diğerleri gibi Yansıma aracılığıyla erişilmesine izin verir ve yapı türünü refparametre olarak alan statik bir temsilci oluşturmak için kullanılır [böyle bir şey miras alınmayan yapı yöntemleriyle çalışır], ancak ben böyle bir temsilci oluşturmayı denedim ve işe yaramadı. Bir yapının ToString()yöntemi için statik bir temsilci oluşturmak mümkün müdür ve eğer öyleyse parametre türü ne olmalıdır?
supercat

Bağlantılar koptu.
OfirD

5

Değer türünde sanal olmayan GetType () yöntemini çağırma:

struct S { };
S s = new S();
s.GetType();

2
GetTypesadece sanal olmadığı için değil, aynı zamanda değer türü depolama konumlarının, yığın nesnelerinin aksine GetType(), türlerini tanımlamak için kullanabilecek "gizli" bir alana sahip olmaması nedeniyle kutulama gerektirir .
supercat

@supercat Hmmm. 1. Derleyici tarafından eklenen kutulama ve çalışma zamanı tarafından kullanılan gizli alan. Derleyici, çalışma zamanı hakkında bilgi sahibi olduğu için kutu ekleme olabilir… 2. Enum değerinde sanal olmayan ToString (string) çağırdığımızda, kutulama da gerektirir ve derleyicinin bunu eklediğine inanmıyorum çünkü Enum.ToString (string) uygulama ayrıntılarını biliyor . Yani sanırım "base value type" üzerinde sanal olmayan yöntem çağrıldığında boks her zaman gerçekleşti diyebilirim.
Viacheslav Ivanov

EnumKendi başına herhangi bir sanal olmayan yönteme sahip olmayı düşünmemiştim , ancak bir ToString()yöntemin Enumtür bilgilerine erişmesi gerekiyordu. Acaba Object, ValueTypeya Enumtip bilgileri olmaksızın işlerini gerçekleştirdiği herhangi olmayan sanal yöntemleri vardır.
supercat

3

Motti'nin cevabında bahsedildi, sadece kod örnekleriyle açıklandı:

İlgili parametreler

public void Bla(object obj)
{

}

Bla(valueType)

public void Bla(IBla i) //where IBla is interface
{

}

Bla(valueType)

Ama bu güvenlidir:

public void Bla<T>(T obj) where T : IBla
{

}

Bla(valueType)

Dönüş türü

public object Bla()
{
    return 1;
}

public IBla Bla() //IBla is an interface that 1 inherits
{
    return 1;
}

Kısıtlanmamış T'yi boşa karşı kontrol etme

public void Bla<T>(T obj)
{
    if (obj == null) //boxes.
}

Dinamik kullanımı

dynamic x = 42; (boxes)

Bir diğeri

enumValue.HasFlag


0
  • Veya System.Collectionsgibi genel olmayan koleksiyonları kullanma .ArrayListHashTable

Kabul edildiğinde, bunlar ilk vakanızın belirli örnekleridir, ancak bunlar gizli sorunlar olabilir. Bugün hala karşılaştığım List<T>ve bunları ve yerine bunları kullanan kod miktarı şaşırtıcı Dictionary<TKey,TValue>.


0

ArrayList'e herhangi bir değer türü değeri eklemek kutulamaya neden olur:

ArrayList items = ...
numbers.Add(1); // boxing to object
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.