Pertanyaan Kemunculan Tinju di C #


Saya mencoba mengumpulkan semua situasi di mana tinju terjadi di C #:

  • Mengonversi jenis nilai menjadi System.Object mengetik:

    struct S { }
    object box = new S();
    
  • Mengonversi jenis nilai menjadi System.ValueType mengetik:

    struct S { }
    System.ValueType box = new S();
    
  • Mengonversi nilai tipe enumerasi ke System.Enum mengetik:

    enum E { A }
    System.Enum box = E.A;
    
  • Mengonversi jenis nilai ke referensi antarmuka:

    interface I { }
    struct S : I { }
    I box = new S();
    
  • Menggunakan tipe nilai dalam rangkaian string C #:

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

    catatan: konstanta dari char jenis digabung pada waktu kompilasi

    catatan: sejak kompiler versi 6.0 C # mengoptimalkan penggabungan melibatkan bool, char, IntPtr, UIntPtr jenis

  • Membuat delegasi dari metode instance jenis nilai:

    struct S { public void M() {} }
    Action box = new S().M;
    
  • Memanggil metode virtual non-override pada jenis nilai:

    enum E { A }
    E.A.GetHashCode();
    
  • Menggunakan pola konstan C # 7.0 di bawah is ekspresi:

    int x = …;
    if (x is 42) { … } // boxes both 'x' and '42'!
    
  • Tinju di C # tuple jenis konversi:

    (int, byte) _tuple;
    
    public (object, object) M() {
      return _tuple; // 2x boxing
    }
    
  • Parameter opsional dari object ketik dengan nilai default jenis nilai:

    void M([Optional, DefaultParameterValue(42)] object o);
    M(); // boxing at call-site
    
  • Memeriksa nilai tipe generik yang tidak dibatasi untuk null:

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

    catatan: ini dapat dioptimalkan oleh JIT pada beberapa runtime NET

  • Ketik nilai pengujian tidak dibatasi atau struct tipe umum dengan is/as operator:

    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);
    

    catatan: ini dapat dioptimalkan oleh JIT pada beberapa runtime NET

Apakah ada lagi situasi tinju, mungkin tersembunyi, yang Anda ketahui?


76
2017-11-03 13:22


asal


Jawaban:


Itu pertanyaan yang bagus!

Tinju terjadi karena satu alasan: ketika kita membutuhkan referensi ke jenis nilai. Semua yang Anda cantumkan termasuk dalam aturan ini.

Misalnya karena objek adalah tipe referensi, mentransmisikan tipe nilai ke objek membutuhkan referensi ke jenis nilai, yang menyebabkan tinju.

Jika Anda ingin membuat daftar setiap skenario yang mungkin, Anda juga harus menyertakan derivatif, seperti mengembalikan tipe nilai dari metode yang mengembalikan objek atau jenis antarmuka, karena ini secara otomatis melemparkan jenis nilai ke objek / antarmuka.

By the way, kasus string string yang Anda identifikasi dengan jelas juga berasal dari casting ke objek. Operator + diterjemahkan oleh kompiler ke panggilan ke metode Concat string, yang menerima objek untuk jenis nilai yang Anda lewati, jadi transmisi ke objek dan karenanya tinju terjadi.

Selama bertahun-tahun saya selalu menyarankan pengembang untuk mengingat satu alasan tinju (yang saya sebutkan di atas) daripada menghafal setiap kasus, karena daftar itu panjang dan sulit diingat. Ini juga mendorong pemahaman tentang apa yang dikodekan oleh kode IL yang dihasilkan untuk kode C # kita (misalnya + pada string menghasilkan panggilan ke String.Concat). Ketika Anda ragu dengan apa yang dihasilkan kompilator dan jika tinju terjadi, Anda dapat menggunakan IL Disassembler (ILDASM.exe). Biasanya Anda harus mencari opcode kotak (hanya ada satu kasus ketika tinju mungkin terjadi meskipun IL tidak menyertakan opcode kotak, lebih detail di bawah).

Tapi saya setuju bahwa beberapa kejadian tinju kurang jelas. Anda mencantumkan salah satunya: memanggil metode non-override dari jenis nilai. Sebenarnya, ini kurang jelas karena alasan lain: ketika Anda memeriksa kode IL Anda tidak melihat opcode kotak, tetapi opcode kendala, sehingga bahkan di IL itu tidak jelas bahwa tinju terjadi! Saya tidak akan mengetahui secara pasti mengapa untuk mencegah jawaban ini menjadi lebih panjang ...

Kasus lain untuk tinju yang kurang jelas adalah ketika memanggil metode kelas dasar dari sebuah struct. Contoh:

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

Di sini ToString ditimpa, sehingga memanggil ToString di MyValType tidak akan menghasilkan tinju. Namun, implementasi panggilan basis ToString dan yang menyebabkan tinju (periksa IL!).

By the way, dua skenario tinju yang tidak jelas ini juga berasal dari aturan tunggal di atas. Ketika sebuah metode dipanggil pada kelas dasar dari suatu tipe nilai, harus ada sesuatu untuk ini kata kunci untuk dirujuk. Karena kelas dasar tipe nilai adalah (selalu) tipe referensi, maka ini kata kunci harus merujuk ke tipe referensi, jadi kita perlu referensi ke jenis nilai dan tinju terjadi karena aturan tunggal.

Berikut adalah tautan langsung ke bagian dari kursus .NET online saya yang membahas tinju secara detail: http://motti.me/mq

Jika Anda hanya tertarik pada skenario tinju yang lebih maju di sini adalah tautan langsung di sana (meskipun tautan di atas akan membawa Anda ke sana juga setelah membahas hal-hal yang lebih mendasar): http://motti.me/mu

Saya harap ini membantu!

Motti


40
2017-11-03 15:32



Memanggil metode GetType () non-virtual pada tipe nilai:

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

5
2017-11-03 15:20



Disebutkan dalam jawaban Motti, hanya menggambarkan dengan contoh kode:

Parameter yang terlibat

public void Bla(object obj)
{

}

Bla(valueType)

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

}

Bla(valueType)

Tapi ini aman:

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

}

Bla(valueType)

Jenis kembali

public object Bla()
{
    return 1;
}

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

Memeriksa T tidak terbatas terhadap null

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

Penggunaan dinamis

dynamic x = 42; (boxes)

Yang lainnya

enumValue.HasFlag


1
2018-04-16 09:49



  • Menggunakan koleksi non-generik di System.Collections seperti ArrayList atau HashTable.

Diberikan ini adalah contoh spesifik dari kasus pertama Anda, tetapi mereka bisa menjadi gotchas tersembunyi. Ini luar biasa jumlah kode yang saya temui saat ini yang menggunakan ini sebagai gantinya List<T> dan Dictionary<TKey,TValue>.


0
2017-11-03 15:40



Menambahkan nilai jenis nilai apa pun ke dalam ArrayList menyebabkan tinju:

ArrayList items = ...
numbers.Add(1); // boxing to object

0
2017-11-03 21:51