Apa kasus sudut teraneh yang pernah Anda lihat di C # atau .NET? [Tutup]

322

Saya mengumpulkan beberapa kotak sudut dan asah otak dan selalu ingin mendengar lebih banyak. Halaman ini hanya mencakup bit bahasa C # dan bobs, tapi saya juga menemukan inti .NET juga menarik. Sebagai contoh, ini adalah salah satu yang tidak ada di halaman, tetapi menurut saya luar biasa:

string x = new string(new char[0]);
string y = new string(new char[0]);
Console.WriteLine(object.ReferenceEquals(x, y));

Saya berharap untuk mencetak False - lagipula, "baru" (dengan tipe referensi) selalu membuat objek baru, bukan? Spesifikasi untuk C # dan CLI menunjukkan bahwa seharusnya. Ya, tidak dalam kasus khusus ini. Ini mencetak True, dan telah dilakukan pada setiap versi kerangka kerja yang telah saya uji. (Aku belum mencobanya pada Mono, diakui ...)

Untuk lebih jelasnya, ini hanya contoh dari hal yang saya cari - saya tidak terlalu mencari diskusi / penjelasan tentang keanehan ini. (Ini tidak sama dengan interning string normal; khususnya, string interning tidak biasanya terjadi ketika konstruktor dipanggil.) Saya benar-benar meminta perilaku aneh yang serupa.

Ada permata lain yang bersembunyi di sana?

Jon Skeet
sumber
64
Diuji pada Mono 2.0 rc; mengembalikan True
Marc Gravell
10
kedua string akhirnya menjadi string.Kosong dan tampaknya kerangka kerja hanya menyimpan satu referensi untuk itu
Adrian Zanescu
34
Ini adalah hal konservasi memori. Cari dokumentasi MSDN untuk string metode statis. Internet. CLR memelihara kumpulan string. Itu sebabnya string dengan konten yang identik muncul sebagai referensi ke objek yaitu memori yang sama.
John Leidegren
12
@ John: String interning hanya terjadi secara otomatis untuk literal . Bukan itu masalahnya di sini. @DanielSwe: Interning tidak diperlukan untuk membuat string tidak berubah. Fakta bahwa itu mungkin adalah akibat wajar dari kekekalan, tetapi magang normal tidak terjadi di sini.
Jon Skeet
3
Detail implementasi yang menyebabkan perilaku ini dijelaskan di sini: blog.liranchen.com/2010/08/brain-teasing-with-strings.html
Liran

Jawaban:

394

Saya pikir saya menunjukkan yang ini sebelumnya, tapi saya suka kesenangannya di sini - ini butuh beberapa debug untuk melacak! (kode asli jelas lebih kompleks dan halus ...)

    static void Foo<T>() where T : new()
    {
        T t = new T();
        Console.WriteLine(t.ToString()); // works fine
        Console.WriteLine(t.GetHashCode()); // works fine
        Console.WriteLine(t.Equals(t)); // works fine

        // so it looks like an object and smells like an object...

        // but this throws a NullReferenceException...
        Console.WriteLine(t.GetType());
    }

Jadi apa yang ...

Jawab: apa saja Nullable<T>- seperti int?. Semua metode diganti, kecuali GetType () yang tidak bisa; jadi itu dilemparkan (kotak) ke objek (dan karenanya ke nol) untuk memanggil objek.GetType () ... yang memanggil null ;-p


Perbarui: plot mengental ... Ayende Rahien melemparkan tantangan serupa di blog-nya , tetapi dengan where T : class, new():

private static void Main() {
    CanThisHappen<MyFunnyType>();
}

public static void CanThisHappen<T>() where T : class, new() {
    var instance = new T(); // new() on a ref-type; should be non-null, then
    Debug.Assert(instance != null, "How did we break the CLR?");
}

Tapi itu bisa dikalahkan! Menggunakan tipuan yang sama yang digunakan oleh hal-hal seperti remoting; peringatan - berikut ini adalah kejahatan murni :

class MyFunnyProxyAttribute : ProxyAttribute {
    public override MarshalByRefObject CreateInstance(Type serverType) {
        return null;
    }
}
[MyFunnyProxy]
class MyFunnyType : ContextBoundObject { }

Dengan ini di tempat, new()panggilan dialihkan ke proksi ( MyFunnyProxyAttribute), yang kembali null. Sekarang pergi dan cuci matamu!

Marc Gravell
sumber
9
Mengapa Nullable <T> .GetType () tidak dapat didefinisikan? Tidakkah seharusnya hasilnya berupa typeof (Nullable <T>)?
Drew Noakes
69
Drew: masalahnya adalah GetType () bukan virtual, jadi tidak ditimpa - yang artinya nilainya dikotak untuk pemanggilan metode. Kotak menjadi referensi nol, karenanya NRE.
Jon Skeet
10
@Drew; selain itu, ada aturan tinju khusus untuk Nullable <T>, yang berarti bahwa kotak kosong Nullable <T> menjadi nol, bukan kotak yang berisi Nullable kosong <T> (dan null membatalkan kotak ke Nullable kosong <T> >)
Marc Gravell
29
Sangat sangat keren. Dengan cara yang tidak keren. ;-)
Konrad Rudolph
6
Konstruktor-kendala, 10.1.5 di C # 3.0 langauge spesifikasi
Marc Gravell
216

Pembulatan Bankir.

Yang ini bukan kompiler bug atau malfungsi, tapi tentu saja kasus sudut yang aneh ...

.Net Framework menggunakan skema atau pembulatan yang dikenal sebagai Pembulatan Bankir.

Dalam Pembulatan Bankir, 0,5 angka dibulatkan ke angka genap terdekat, jadi

Math.Round(-0.5) == 0
Math.Round(0.5) == 0
Math.Round(1.5) == 2
Math.Round(2.5) == 2
etc...

Hal ini dapat menyebabkan beberapa bug yang tidak terduga dalam perhitungan keuangan berdasarkan pembulatan Round-Half-Up yang lebih dikenal.

Ini juga berlaku untuk Visual Basic.

Samuel Kim
sumber
22
Rasanya aneh juga bagi saya. Setidaknya, sampai saya memiliki daftar besar angka dan menghitung jumlah mereka. Anda kemudian menyadari bahwa jika Anda hanya mengumpulkan, Anda akan berakhir dengan potensi perbedaan besar dari jumlah angka yang tidak bulat. Sangat buruk jika Anda melakukan perhitungan keuangan!
Tsvetomir Tsonev
255
Jika orang tidak tahu, Anda bisa melakukannya: Math.Round (x, MidpointRounding.AwayFromZero); Untuk mengubah skema pembulatan.
ICR
26
Dari dokumen: Perilaku metode ini mengikuti IEEE Standard 754, bagian 4. Pembulatan semacam ini kadang-kadang disebut pembulatan ke terdekat, atau pembulatan bankir. Ini meminimalkan kesalahan pembulatan yang dihasilkan dari pembulatan nilai titik tengah secara konsisten dalam satu arah.
ICR
8
Saya bertanya-tanya apakah ini sebabnya saya int(fVal + 0.5)sering melihat bahkan dalam bahasa yang memiliki fungsi pembulatan bawaan.
Ben Blank
32
Ironisnya, saya pernah bekerja di bank dan programmer lain mulai membolak-balik tentang hal ini, berpikir bahwa pembulatan rusak dalam kerangka kerja
dan
176

Apa yang akan fungsi ini lakukan jika dipanggil sebagai Rec(0)(tidak di bawah debugger)?

static void Rec(int i)
{
    Console.WriteLine(i);
    if (i < int.MaxValue)
    {
        Rec(i + 1);
    }
}

Menjawab:

  • Pada JIT 32-bit itu akan menghasilkan StackOverflowException
  • Pada JIT 64-bit, ia harus mencetak semua angka ke int.MaxValue

Ini karena kompiler JIT 64-bit menerapkan optimisasi panggilan , sedangkan JIT 32-bit tidak.

Sayangnya saya belum punya mesin 64-bit untuk memverifikasi ini, tetapi metode ini tidak memenuhi semua persyaratan untuk optimasi panggilan ekor. Jika ada yang punya, saya akan tertarik untuk melihat apakah itu benar.

Greg Beech
sumber
10
Harus dikompilasi dalam mode rilis, tetapi yang paling pasti bekerja pada x64 =)
Neil Williams
3
mungkin layak memperbarui jawaban Anda ketika VS 2010 keluar karena semua JIT saat ini kemudian akan melakukan TCO dalam mode Rilis
ShuggyCoUk
3
Baru saja mencoba pada VS2010 Beta 1 pada WinXP 32-bit. Masih mendapatkan StackOverflowException.
squillman
130
+1 untuk StackOverflowException
calvinlough
7
Bahwa di ++sana benar-benar mengusir saya. Tidak bisakah kamu menelepon Rec(i + 1)seperti orang normal?
konfigurator
111

Tetapkan Ini!


Ini adalah salah satu yang ingin saya tanyakan di pesta-pesta (yang mungkin mengapa saya tidak diundang lagi):

Bisakah Anda membuat kompilasi kode berikut?

    public void Foo()
    {
        this = new Teaser();
    }

Cheat yang mudah bisa berupa:

string cheat = @"
    public void Foo()
    {
        this = new Teaser();
    }
";

Tetapi solusi sebenarnya adalah ini:

public struct Teaser
{
    public void Foo()
    {
        this = new Teaser();
    }
}

Jadi sedikit fakta bahwa tipe nilai (struct) dapat menetapkan ulang thisvariabel mereka .

Omer Mor
sumber
3
Kelas C ++ dapat melakukan itu juga ... seperti yang saya temukan baru-baru ini, hanya untuk dimarahi karena benar-benar mencoba menggunakannya untuk optimasi: p
mpen
1
Saya menggunakan in-place baru sebenarnya. Hanya ingin cara yang efisien untuk memperbarui semua bidang :)
mpen
70
Ini juga cheat: //this = new Teaser();:-)
AndrewJacksonZA
17
:-) Saya lebih suka kode curang itu dalam kode produksi saya, daripada kekejian penugasan ulang ini ...
Omer Mor
2
Dari CLR via C #: Alasan mereka membuat ini adalah karena Anda dapat memanggil konstruktor tanpa parameter dari struct di konstruktor lain. Jika Anda hanya ingin menginisialisasi satu nilai struct dan ingin nilai lainnya menjadi nol / nol (default), Anda dapat menulis public Foo(int bar){this = new Foo(); specialVar = bar;}. Ini tidak efisien dan tidak benar-benar dibenarkan ( specialVarditugaskan dua kali), tetapi hanya FYI. (Itulah alasan yang diberikan dalam buku ini, saya tidak tahu mengapa kita tidak seharusnya melakukannya public Foo(int bar) : this())
kizzx2
100

Beberapa tahun yang lalu, ketika mengerjakan program loyalitas, kami memiliki masalah dengan jumlah poin yang diberikan kepada pelanggan. Masalah ini terkait dengan casting / convert double ke int.

Dalam kode di bawah ini:

double d = 13.6;

int i1 = Convert.ToInt32(d);
int i2 = (int)d;

apakah i1 == i2 ?

Ternyata i1! = I2. Karena perbedaan kebijakan pembulatan dalam Konversi dan operator, nilai sebenarnya adalah:

i1 == 14
i2 == 13

Itu selalu lebih baik untuk memanggil Math.Ceiling () atau Math.Floor () (atau Math.Round dengan MidpointRounding yang memenuhi persyaratan kami)

int i1 = Convert.ToInt32( Math.Ceiling(d) );
int i2 = (int) Math.Ceiling(d);
Jarek Kardas
sumber
44
Casting ke integer tidak bulat, itu hanya memotongnya (secara efektif selalu membulatkan ke bawah). Jadi ini masuk akal.
Max Schmeling
57
@ Max: ya, tapi mengapa Convert round?
Stefan Steinegger
18
@Stefan Steinegger Jika semua itu dilakukan, tidak akan ada alasan untuk itu, bukan? Perhatikan juga bahwa nama kelas adalah Konversi bukan Pemain.
bug-a-lot
3
Dalam VB: putaran CInt (). Perbaiki () terpotong. Burned me once ( blog.wassupy.com/2006/01/i-can-believe-it-not-truncating.html )
Michael Haren
74

Mereka seharusnya membuat 0 bilangan bulat bahkan ketika ada fungsi enum yang berlebihan.

Saya tahu dasar pemikiran tim inti C # untuk memetakan 0 hingga enum, tapi tetap saja, itu tidak sesortogonal sebagaimana seharusnya. Contoh dari Npgsql .

Contoh uji:

namespace Craft
{
    enum Symbol { Alpha = 1, Beta = 2, Gamma = 3, Delta = 4 };


   class Mate
    {
        static void Main(string[] args)
        {

            JustTest(Symbol.Alpha); // enum
            JustTest(0); // why enum
            JustTest((int)0); // why still enum

            int i = 0;

            JustTest(Convert.ToInt32(0)); // have to use Convert.ToInt32 to convince the compiler to make the call site use the object version

            JustTest(i); // it's ok from down here and below
            JustTest(1);
            JustTest("string");
            JustTest(Guid.NewGuid());
            JustTest(new DataTable());

            Console.ReadLine();
        }

        static void JustTest(Symbol a)
        {
            Console.WriteLine("Enum");
        }

        static void JustTest(object o)
        {
            Console.WriteLine("Object");
        }
    }
}
Michael Buen
sumber
18
Wow itu yang baru bagi saya. Juga aneh bagaimana ConverTo.ToIn32 () bekerja tetapi melakukan casting ke (int) 0 tidak. Dan angka lainnya> 0 berfungsi. (Dengan "berhasil", maksud saya sebut objek itu kelebihan.)
Lucas
Ada aturan analisis kode anjuran untuk menegakkan praktik-praktik yang baik di sekitar perilaku ini: msdn.microsoft.com/en-us/library/ms182149%28VS.80%29.aspx Tautan itu juga berisi deskripsi yang bagus tentang cara kerja pemetaan-0 bekerja .
Chris Clark
1
@ Chris Clark: saya mencoba menempatkan None = 0 pada Enum Symbol. masih kompiler memilih enum untuk 0 dan genap (int) 0
Michael Buen
2
IMO mereka seharusnya memperkenalkan kata kunci noneyang dapat digunakan dikonversi ke enum apa pun, dan menjadikan 0 selalu int dan tidak secara implisit dikonversi menjadi enum.
CodesInChaos
5
ConverTo.ToIn32 () berfungsi karena hasilnya tidak ada konstanta kompilasi. Dan hanya konstanta compiletime 0 yang dapat dikonversi menjadi enum. Dalam versi sebelumnya dari .net bahkan hanya literal 0seharusnya dapat dikonversi menjadi enum. Lihat Eric Lippert Blog: blogs.msdn.com/b/ericlippert/archive/2006/03/28/563282.aspx
CodesInChaos
67

Ini adalah salah satu yang paling tidak biasa yang pernah saya lihat sejauh ini (selain dari yang di sini tentu saja!):

public class Turtle<T> where T : Turtle<T>
{
}

Ini memungkinkan Anda mendeklarasikannya tetapi tidak memiliki kegunaan nyata, karena ia akan selalu meminta Anda untuk membungkus kelas apa pun yang Anda isi di tengah dengan Turtle lainnya.

[lelucon] Kurasa itu kura-kura sampai habis ... [/ bercanda]

RCIX
sumber
34
Anda dapat membuat instance, meskipun:class RealTurtle : Turtle<RealTurtle> { } RealTurtle t = new RealTurtle();
Marc Gravell
24
Memang. Ini adalah pola yang digunakan Java enum untuk efek luar biasa. Saya menggunakannya dalam Protokol Buffer juga.
Jon Skeet
6
RCIX, oh ya itu.
Joshua
8
Saya telah menggunakan pola ini cukup banyak dalam hal obat generik mewah. Ini memungkinkan hal-hal seperti klon yang diketik dengan benar, atau membuat instance dari dirinya sendiri.
Lucero
20
Ini adalah 'pola templat yang berulang berulang' en.wikipedia.org/wiki/Curiously_recurring_template_pattern
porges
65

Inilah yang baru saya ketahui tentang baru-baru ini ...

interface IFoo
{
   string Message {get;}
}
...
IFoo obj = new IFoo("abc");
Console.WriteLine(obj.Message);

Sekilas di atas terlihat gila, tetapi sebenarnya legal. Tidak , sungguh (walaupun saya telah melewatkan bagian penting, tapi itu bukan sesuatu yang gila seperti "tambahkan kelas yang disebut IFoo" atau "tambahkan usingalias untuk menunjuk IFoopada sebuah kelas").

Lihat apakah Anda dapat mengetahui alasannya, lalu: Siapa bilang Anda tidak bisa membuat instance antarmuka?

Marc Gravell
sumber
1
1 untuk "menggunakan alias" - Aku tidak pernah tahu Anda bisa melakukan itu !
David
retas dalam kompiler untuk COM Interop :-)
Ion Todirel
Anda bajingan! Paling tidak Anda bisa mengatakan "dalam keadaan tertentu" ... Kompiler saya salah!
MA Hanin 8/11
56

Kapan seorang Boolean tidak Benar atau Salah?

Bill menemukan bahwa Anda dapat meretas boolean sehingga jika A benar dan B benar, (A dan B) salah.

Boolean yang diretas

Jonathan Allen
sumber
134
Ketika itu FILE_NOT_FOUND, tentu saja!
Greg
12
Ini menarik karena secara matematis berarti bahwa tidak ada pernyataan dalam C # yang dapat dibuktikan. Ups.
Simon Johnson
20
Suatu hari saya akan menulis sebuah program yang tergantung pada perilaku ini, dan setan-setan neraka yang paling gelap akan mempersiapkan sambutan untuk saya. Bwahahahahaha!
Jeffrey L Whitledge
18
Contoh ini menggunakan bitwise, bukan operator yang logis. Bagaimana itu mengejutkan?
Josh Lee
6
Yah, dia meretas tata letak struct, tentu saja Anda akan mendapatkan hasil yang aneh, ini tidak mengejutkan atau tidak terduga!
Ion Todirel
47

Saya datang agak terlambat ke pesta, tapi saya punya tiga empat lima:

  1. Jika Anda polling Invoke Diperlukan pada kontrol yang belum dimuat / ditampilkan, itu akan mengatakan palsu - dan meledak di wajah Anda jika Anda mencoba mengubahnya dari utas lainnya ( solusinya adalah untuk referensi ini. Tangani pencipta kontrol).

  2. Satu lagi yang membuat saya tersandung adalah bahwa diberikan pertemuan dengan:

    enum MyEnum
    {
        Red,
        Blue,
    }

    jika Anda menghitung MyEnum.Red.ToString () di majelis lain, dan di sela waktu seseorang telah mengkompilasi ulang enum Anda ke:

    enum MyEnum
    {
        Black,
        Red,
        Blue,
    }

    saat runtime, Anda akan mendapatkan "Hitam".

  3. Saya memiliki sebuah majelis bersama dengan beberapa konstanta yang berguna. Pendahulu saya telah meninggalkan banyak properti hanya-mendapatkan yang tampak jelek, saya pikir saya akan menyingkirkan kekacauan dan hanya menggunakan const publik. Saya sangat terkejut ketika VS mengkompilasi nilai-nilai mereka, dan bukan referensi.

  4. Jika Anda menerapkan metode baru antarmuka dari perakitan lain, tetapi Anda membangun kembali referensi versi lama perakitan itu, Anda mendapatkan TypeLoadException (tidak ada implementasi 'Metode Baru'), meskipun Anda telah mengimplementasikannya (lihat di sini ).

  5. Kamus <,>: "Urutan pengembalian barang tidak ditentukan". Ini mengerikan , karena kadang-kadang bisa menggigit Anda, tetapi bekerja pada orang lain, dan jika Anda baru saja secara buta berasumsi bahwa Kamus akan bermain bagus ("mengapa tidak? Saya pikir, List tidak"), Anda benar-benar harus hidung Anda sebelum akhirnya mulai mempertanyakan asumsi Anda.

Benjol
sumber
6
# 2 adalah contoh yang menarik. Enum adalah pemetaan kompiler ke nilai integral. Jadi, meskipun Anda tidak secara eksplisit menetapkan nilai, kompilator melakukannya, menghasilkan MyEnum.Red = 0 dan MyEnum.Blue = 1. Ketika Anda menambahkan Hitam, Anda mendefinisikan kembali nilai 0 untuk memetakan dari Merah ke Hitam. Saya menduga bahwa masalah akan terwujud dalam penggunaan lain juga, seperti Serialisasi.
LBushkin
3
Diperlukan +1 untuk Invoke. Di milik kami, kami lebih suka menetapkan nilai secara eksplisit ke enum seperti Merah = 1, Biru = 2 sehingga yang baru dapat dimasukkan sebelum atau setelah itu akan selalu menghasilkan nilai yang sama. Khususnya diperlukan jika Anda menyimpan nilai ke database.
TheVillageIdiot
53
Saya tidak setuju bahwa # 5 adalah "kasus tepi". Kamus seharusnya tidak memiliki urutan yang ditentukan berdasarkan kapan Anda memasukkan nilai. Jika Anda menginginkan pesanan yang ditentukan, gunakan Daftar, atau gunakan kunci yang dapat diurutkan dengan cara yang bermanfaat bagi Anda, atau gunakan struktur data yang sama sekali berbeda.
Wedge
21
@Wedge, mungkin seperti SortedDictionary?
Allon Guralnek
4
# 3 terjadi karena konstanta dimasukkan sebagai literal di mana pun mereka digunakan (dalam C #, setidaknya). Pendahulu Anda mungkin sudah menyadarinya, itulah sebabnya mereka menggunakan properti get-only. Namun, variabel readonly (sebagai lawan dari const) akan bekerja dengan baik.
Remoun
33

VB.NET, nullables dan operator ternary:

Dim i As Integer? = If(True, Nothing, 5)

Ini membutuhkan waktu untuk melakukan debug, karena saya diharapkan iuntuk memuatnya Nothing.

Apa yang sebenarnya saya isi? 0.

Ini mengejutkan tetapi sebenarnya perilaku "benar": Nothingdi VB.NET tidak persis sama dengan nulldi CLR: Nothingbisa berarti nullatau default(T)untuk tipe nilai T, tergantung pada konteksnya. Dalam kasus di atas, Ifmenyimpulkan Integersebagai jenis umum Nothingdan 5, jadi, dalam hal ini, Nothingberarti 0.

Heinzi
sumber
Cukup menarik, saya gagal menemukan jawaban ini sehingga saya harus membuat pertanyaan . Nah, siapa yang tahu jawabannya ada di utas ini?
GSerg
28

Saya menemukan kasus sudut benar-benar aneh yang mengalahkan yang pertama dengan tembakan panjang.

Metode String.Equals (String, String, StringComparison) sebenarnya tidak bebas efek samping.

Saya sedang mengerjakan blok kode yang memiliki ini pada baris dengan sendirinya di bagian atas beberapa fungsi:

stringvariable1.Equals(stringvariable2, StringComparison.InvariantCultureIgnoreCase);

Menghapus garis yang mengarah ke stack overflow di tempat lain dalam program ini.

Kode tersebut ternyata menginstal handler untuk apa yang pada dasarnya adalah acara BeforeAssemblyLoad dan coba lakukan

if (assemblyfilename.EndsWith("someparticular.dll", StringComparison.InvariantCultureIgnoreCase))
{
    assemblyfilename = "someparticular_modified.dll";
}

Sekarang aku seharusnya tidak memberitahumu. Menggunakan budaya yang belum pernah digunakan sebelumnya dalam perbandingan string menyebabkan beban perakitan. InvariantCulture bukan pengecualian untuk ini.

Joshua
sumber
Saya kira "memuat perakitan" adalah efek samping, karena Anda dapat mengamatinya dengan BeforeAssemblyLoad!
Jacob Krall
2
Wow. Ini adalah tembakan yang sempurna ke kaki penjaga. Saya kira menulis pawang BeforeAssemblyLoad dapat menyebabkan banyak kejutan seperti itu.
2014
20

Berikut adalah contoh bagaimana Anda dapat membuat struct yang menyebabkan pesan kesalahan "Mencoba membaca atau menulis memori yang dilindungi. Ini sering merupakan indikasi bahwa memori lain rusak". Perbedaan antara sukses dan gagal sangat halus.

Tes unit berikut menunjukkan masalahnya.

Lihat apakah Anda dapat menemukan kesalahan.

    [Test]
    public void Test()
    {
        var bar = new MyClass
        {
            Foo = 500
        };
        bar.Foo += 500;

        Assert.That(bar.Foo.Value.Amount, Is.EqualTo(1000));
    }

    private class MyClass
    {
        public MyStruct? Foo { get; set; }
    }

    private struct MyStruct
    {
        public decimal Amount { get; private set; }

        public MyStruct(decimal amount) : this()
        {
            Amount = amount;
        }

        public static MyStruct operator +(MyStruct x, MyStruct y)
        {
            return new MyStruct(x.Amount + y.Amount);
        }

        public static MyStruct operator +(MyStruct x, decimal y)
        {
            return new MyStruct(x.Amount + y);
        }

        public static implicit operator MyStruct(int value)
        {
            return new MyStruct(value);
        }

        public static implicit operator MyStruct(decimal value)
        {
            return new MyStruct(value);
        }
    }
cbp
sumber
Kepalaku sakit ... Mengapa ini tidak berhasil?
jasonh
2
Hm saya menulis ini beberapa bulan yang lalu, tetapi saya tidak ingat mengapa persisnya itu terjadi.
cbp
10
Tampak seperti bug penyusun; yang += 500panggilan: ldc.i4 500(mendorong 500 sebagai Int32), kemudian call valuetype Program/MyStruct Program/MyStruct::op_Addition(valuetype Program/MyStruct, valuetype [mscorlib]System.Decimal)- sehingga kemudian memperlakukan sebagai decimal(96-bit) tanpa konversi apapun. Jika Anda menggunakannya += 500Mdengan benar. Itu hanya terlihat seperti kompiler berpikir itu dapat melakukannya dengan satu cara (mungkin karena operator int implisit) dan kemudian memutuskan untuk melakukannya dengan cara lain.
Marc Gravell
1
Maaf untuk pos ganda, berikut penjelasan yang lebih berkualitas. Saya akan menambahkan ini, saya sedikit kesal dengan ini dan ini menyebalkan, meskipun saya mengerti mengapa itu terjadi. Bagi saya ini adalah batasan yang disayangkan dari struct / valuetype. bytes.com/topic/net/answers/…
Bennett Dill
2
@Ben mendapatkan kesalahan kompiler atau modifikasi yang tidak mempengaruhi struct asli baik-baik saja. Pelanggaran akses adalah binatang yang sangat berbeda. Runtime tidak boleh membuangnya jika Anda hanya menulis kode yang dikelola murni aman.
CodesInChaos
18

C # mendukung konversi antara array dan daftar selama array tidak multidimensi dan ada hubungan warisan antara tipe dan tipe adalah tipe referensi

object[] oArray = new string[] { "one", "two", "three" };
string[] sArray = (string[])oArray;

// Also works for IList (and IEnumerable, ICollection)
IList<string> sList = (IList<string>)oArray;
IList<object> oList = new string[] { "one", "two", "three" };

Perhatikan bahwa ini tidak berhasil:

object[] oArray2 = new int[] { 1, 2, 3 }; // Error: Cannot implicitly convert type 'int[]' to 'object[]'
int[] iArray = (int[])oArray2;            // Error: Cannot convert type 'object[]' to 'int[]'
Peter van der Heijden
sumber
11
Contoh IList <T> hanyalah gips, karena string [] sudah mengimplementasikan ICloneable, IList, ICollection, IEnumerable, IList <string>, ICollection <string>, dan IEnumerable <string>.
Lucas
15

Ini adalah hal teraneh yang pernah saya alami:

public class DummyObject
{
    public override string ToString()
    {
        return null;
    }
}

Digunakan sebagai berikut:

DummyObject obj = new DummyObject();
Console.WriteLine("The text: " + obj.GetType() + " is " + obj);

Akan melempar NullReferenceException. Ternyata beberapa tambahan dikompilasi oleh kompiler C # ke panggilan String.Concat(object[]). Sebelum .NET 4, ada bug dalam kelebihan Concat di mana objek diperiksa untuk null, tetapi bukan hasil dari ToString ():

object obj2 = args[i];
string text = (obj2 != null) ? obj2.ToString() : string.Empty;
// if obj2 is non-null, but obj2.ToString() returns null, then text==null
int length = text.Length;

Ini adalah bug oleh ECMA-334 §14.7.4:

Operator biner + melakukan penggabungan string ketika satu atau kedua operan bertipe string. Jika operan penggabungan string null, string kosong diganti. Kalau tidak, setiap operand non-string akan dikonversi ke representasi string dengan memanggil ToStringmetode virtual yang diwarisi dari tipe object. Jika ToStringkembali null, string kosong diganti.

Sam Harwell
sumber
3
Hmm, tapi saya bisa membayangkan kesalahan ini karena .ToStringseharusnya tidak pernah mengembalikan null, tetapi string.Empty. Namun demikian dan kesalahan dalam kerangka tersebut.
Dykam
12

Menarik - ketika saya pertama kali melihat bahwa saya berasumsi itu adalah sesuatu yang sedang diperiksa oleh kompiler C #, tetapi bahkan jika Anda memancarkan IL secara langsung untuk menghilangkan kemungkinan interferensi, hal itu masih terjadi, yang berarti itu adalah newobjop-code yang melakukan memeriksa.

var method = new DynamicMethod("Test", null, null);
var il = method.GetILGenerator();

il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Newarr, typeof(char));
il.Emit(OpCodes.Newobj, typeof(string).GetConstructor(new[] { typeof(char[]) }));

il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Newarr, typeof(char));
il.Emit(OpCodes.Newobj, typeof(string).GetConstructor(new[] { typeof(char[]) }));

il.Emit(OpCodes.Call, typeof(object).GetMethod("ReferenceEquals"));
il.Emit(OpCodes.Box, typeof(bool));
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }));

il.Emit(OpCodes.Ret);

method.Invoke(null, null);

Ini juga sama dengan truejika Anda mengecek terhadap string.Emptyyang berarti op-code ini harus memiliki perilaku khusus untuk menterjemahkan string kosong.

Greg Beech
sumber
bukan untuk menjadi orang yang pintar atau apa pun tetapi pernahkah Anda mendengar tentang reflektor ? cukup berguna dalam kasus-kasus seperti ini;
RCIX
3
Anda tidak pintar; Anda melewatkan intinya - Saya ingin membuat IL khusus untuk kasus yang satu ini. Lagi pula, mengingat bahwa Reflection.Emit sepele untuk skenario jenis ini, mungkin secepat menulis program di C # kemudian membuka reflektor, menemukan biner, menemukan metode, dll ... Dan aku bahkan tidak perlu tinggalkan IDE untuk melakukannya.
Greg Beech
10
Public Class Item
   Public ID As Guid
   Public Text As String

   Public Sub New(ByVal id As Guid, ByVal name As String)
      Me.ID = id
      Me.Text = name
   End Sub
End Class

Public Sub Load(sender As Object, e As EventArgs) Handles Me.Load
   Dim box As New ComboBox
   Me.Controls.Add(box)          'Sorry I forgot this line the first time.'
   Dim h As IntPtr = box.Handle  'Im not sure you need this but you might.'
   Try
      box.Items.Add(New Item(Guid.Empty, Nothing))
   Catch ex As Exception
      MsgBox(ex.ToString())
   End Try
End Sub

Outputnya adalah "Mencoba membaca memori yang dilindungi. Ini merupakan indikasi bahwa memori lain rusak."

Joshua
sumber
1
Menarik! Kedengarannya seperti bug kompiler; Saya telah porting ke C # dan berfungsi dengan baik. Yang mengatakan, ada banyak masalah dengan pengecualian dilemparkan ke Load, dan berperilaku berbeda dengan / tanpa debugger - Anda dapat menangkap dengan debugger, tetapi tidak tanpa (dalam beberapa kasus).
Marc Gravell
Maaf, saya lupa, Anda harus menambahkan kotak kombo ke formulir sebelum itu.
Joshua
Apakah ini berkaitan dengan inisialisasi dialog menggunakan SEH sebagai semacam mekanisme komunikasi internal yang mengerikan? Samar-samar saya ingat sesuatu seperti itu di Win32.
Daniel Earwicker
1
Ini masalah yang sama cbp di atas. The valuetype dikembalikan adalah salinan, karenanya setiap referensi untuk setiap properti yang berasal dari copy mengatakan sedang menuju ke bit-ember tanah ... bytes.com/topic/net/answers/...
Bennett Dill
1
Nggak. Tidak ada struct di sini. Saya sebenarnya men-debug itu. Itu menambahkan NULL ke daftar item koleksi kotak kombo asli yang menyebabkan crash tertunda.
Joshua
10

PropertyInfo.SetValue () dapat menetapkan ints ke enums, ints ke int nullable, enums ke enums nullable, tetapi tidak ints ke enums nullable.

enumProperty.SetValue(obj, 1, null); //works
nullableIntProperty.SetValue(obj, 1, null); //works
nullableEnumProperty.SetValue(obj, MyEnum.Foo, null); //works
nullableEnumProperty.SetValue(obj, 1, null); // throws an exception !!!

Deskripsi lengkap di sini

Anders Ivner
sumber
10

Bagaimana jika Anda memiliki kelas generik yang memiliki metode yang dapat dibuat ambigu tergantung pada tipe argumen? Saya mengalami situasi ini baru-baru ini menulis kamus dua arah. Saya ingin menulis Get()metode simetris yang akan mengembalikan kebalikan dari argumen apa pun yang disahkan. Sesuatu seperti ini:

class TwoWayRelationship<T1, T2>
{
    public T2 Get(T1 key) { /* ... */ }
    public T1 Get(T2 key) { /* ... */ }
}

Semua baik-baik saja jika Anda membuat contoh di mana T1dan T2berbagai jenis:

var r1 = new TwoWayRelationship<int, string>();
r1.Get(1);
r1.Get("a");

Tetapi jika T1dan T2sama (dan mungkin jika satu adalah subkelas dari yang lain), itu adalah kesalahan kompiler:

var r2 = new TwoWayRelationship<int, int>();
r2.Get(1);  // "The call is ambiguous..."

Menariknya, semua metode lain dalam kasus kedua masih dapat digunakan; itu hanya panggilan ke metode sekarang-ambigu yang menyebabkan kesalahan kompiler. Kasus yang menarik, jika agak tidak mungkin dan tidak jelas.

tclem
sumber
Penentang metode overloading akan suka yang ini ^^.
Christian Klauser
1
Saya tidak tahu, ini masuk akal bagi saya.
Scott Whitlock
10

C # Puzzler Aksesibilitas


Kelas turunan berikut mengakses bidang pribadi dari kelas dasarnya, dan kompilator diam-diam melihat ke sisi lain:

public class Derived : Base
{
    public int BrokenAccess()
    {
        return base.m_basePrivateField;
    }
}

Bidang ini memang pribadi:

private int m_basePrivateField = 0;

Ingin menebak bagaimana kita dapat membuat kompilasi kode seperti itu?

.

.

.

.

.

.

.

Menjawab


Kuncinya adalah mendeklarasikan Derivedsebagai kelas dalam dari Base:

public class Base
{
    private int m_basePrivateField = 0;

    public class Derived : Base
    {
        public int BrokenAccess()
        {
            return base.m_basePrivateField;
        }
    }
}

Kelas batin diberi akses penuh ke anggota kelas luar. Dalam hal ini kelas batin juga berasal dari kelas luar. Ini memungkinkan kita untuk "mematahkan" enkapsulasi anggota pribadi.

Omer Mor
sumber
Itu sebenarnya didokumentasikan dengan baik; msdn.microsoft.com/en-us/library/ms173120%28VS.80%29.aspx . Ini dapat menjadi fitur yang bermanfaat di waktu, terutama jika kelas luar statis.
Ya - tentu saja didokumentasikan. Namun, sangat sedikit orang yang memecahkan teka-teki ini, jadi saya pikir ini adalah hal yang sepele.
Omer Mor
2
Sepertinya Anda akan memiliki kemungkinan stack overflow yang sangat kuat dengan memiliki kelas dalam mewarisi pemiliknya ...
Jamie Treworgy
Namun kasus lain yang serupa (dan sangat benar) adalah bahwa suatu objek dapat mengakses anggota pribadi dari objek lain dengan jenis yang sama:class A { private int _i; public void foo(A other) { int res = other._i; } }
Olivier Jacot-Descombes
10

Baru saja menemukan hal kecil yang menyenangkan hari ini:

public class Base
{
   public virtual void Initialize(dynamic stuff) { 
   //...
   }
}
public class Derived:Base
{
   public override void Initialize(dynamic stuff) {
   base.Initialize(stuff);
   //...
   }
}

Ini melempar kesalahan kompilasi.

Panggilan ke metode 'Inisialisasi' perlu dikirim secara dinamis, tetapi tidak bisa karena itu adalah bagian dari ekspresi akses dasar. Pertimbangkan untuk melemparkan argumen dinamis atau menghilangkan akses basis.

Jika saya menulis base.Initialize (stuff as object); itu bekerja dengan sempurna, namun ini tampaknya menjadi "kata ajaib" di sini, karena ia melakukan hal yang persis sama, semuanya masih diterima sebagai dinamis ...

TDaver
sumber
8

Dalam API yang kami gunakan, metode yang mengembalikan objek domain mungkin mengembalikan "objek null" khusus. Dalam implementasi ini, operator perbandingan dan Equals()metode diganti untuk kembali truejika dibandingkan dengan null.

Jadi pengguna API ini mungkin memiliki beberapa kode seperti ini:

return test != null ? test : GetDefault();

atau mungkin sedikit lebih verbose, seperti ini:

if (test == null)
    return GetDefault();
return test;

di mana GetDefault()metode mengembalikan nilai default yang ingin kita gunakan alih-alihnull . Kejutan itu mengejutkan saya ketika saya menggunakan ReSharper dan mengikuti anjurannya untuk menulis ulang salah satu dari ini sebagai berikut:

return test ?? GetDefault();

Jika objek pengujian adalah objek nol yang dikembalikan dari API alih-alih sepantasnya null, perilaku kode sekarang telah berubah, karena operator penggabungan nol benar-benar memeriksa null, tidak menjalankan operator=atau Equals().

Tor Livar
sumber
1
tidak benar-benar ac kasus sudut, tetapi tuan yang berpikir itu?!?
Ray Booysen
Bukankah kode ini hanya menggunakan tipe nullable? Karenanya ReSharper merekomendasikan "??" menggunakan. Seperti kata Ray, aku tidak akan menganggap ini kasus sudut; atau saya salah?
Tony
1
Ya, jenisnya dapat dibatalkan - dan ada juga NullObject. Jika ini adalah kasus sudut, saya tidak tahu, tetapi setidaknya ini adalah kasus di mana 'if (a! = Null) mengembalikan a; kembali b; ' tidak sama dengan 'mengembalikan a ?? b '. Saya benar-benar setuju itu adalah masalah dengan desain framework / API - overloading == null untuk mengembalikan true pada suatu objek tentu saja bukan ide yang bagus!
Tor Livar
8

Pertimbangkan kasus aneh ini:

public interface MyInterface {
  void Method();
}
public class Base {
  public void Method() { }
}
public class Derived : Base, MyInterface { }

Jika Basedan Deriveddideklarasikan dalam rakitan yang sama, kompiler akan membuatnyaBase::Method virtual dan disegel (dalam CIL), meskipun Basetidak mengimplementasikan antarmuka.

Jika Basedan Derivedberada di majelis yang berbeda, saat menyusun Derivedmajelis, kompilator tidak akan mengubah majelis lain, sehingga akan memperkenalkan anggota di dalamnya Derivedyang akan menjadi implementasi eksplisit untukMyInterface::Method itu hanya akan mendelegasikan panggilan ke Base::Method.

Compiler harus melakukan ini untuk mendukung pengiriman polimorfik berkaitan dengan antarmuka, yaitu harus membuat metode itu virtual.

Jordão
sumber
Itu memang terdengar aneh. Harus menyelidiki nanti :)
Jon Skeet
@ Jon Skeet: Saya menemukan ini ketika meneliti strategi implementasi untuk peran dalam C # . Akan sangat bagus untuk mendapatkan tanggapan Anda tentang hal itu!
Jordão
7

Berikut ini mungkin pengetahuan umum yang hanya kurang, tapi eh. Beberapa waktu yang lalu, kami memiliki kasus bug yang termasuk properti virtual. Abstraksi konteksnya sedikit, pertimbangkan kode berikut, dan terapkan breakpoint ke area yang ditentukan:

class Program
{
    static void Main(string[] args)
    {
        Derived d = new Derived();
        d.Property = "AWESOME";
    }
}

class Base
{
    string _baseProp;
    public virtual string Property 
    { 
        get 
        {
            return "BASE_" + _baseProp;
        }
        set
        {
            _baseProp = value;
            //do work with the base property which might 
            //not be exposed to derived types
            //here
            Console.Out.WriteLine("_baseProp is BASE_" + value.ToString());
        }
    }
}

class Derived : Base
{
    string _prop;
    public override string Property 
    {
        get { return _prop; }
        set 
        { 
            _prop = value; 
            base.Property = value;
        } //<- put a breakpoint here then mouse over BaseProperty, 
          //   and then mouse over the base.Property call inside it.
    }

    public string BaseProperty { get { return base.Property; } private set { } }
}

Saat berada dalam Derivedkonteks objek, Anda bisa mendapatkan perilaku yang sama saat menambahkan base.Propertysebagai arloji, atau mengetik base.Propertyke arloji cepat.

Butuh waktu untuk menyadari apa yang sedang terjadi. Pada akhirnya saya tercerahkan oleh Quickwatch. Saat membuka Quickwatch dan menjelajahi Derivedobjek d (atau dari konteks objek, this) dan memilih bidang base, bidang edit di atas Quickwatch menampilkan pemeran berikut:

((TestProject1.Base)(d))

Yang berarti bahwa jika pangkalan diganti seperti itu, panggilan akan menjadi

public string BaseProperty { get { return ((TestProject1.Base)(d)).Property; } private set { } }

untuk Arloji, Quickwatch, dan tooltip debug-mouse, dan kemudian masuk akal untuk menampilkannya "AWESOME"daripada "BASE_AWESOME"mempertimbangkan polimorfisme. Saya masih tidak yakin mengapa itu akan mengubahnya menjadi pemain, satu hipotesis adalah bahwa callmungkin tidak tersedia dari konteks modul-modul itu, dan hanya callvirt.

Bagaimanapun, itu jelas tidak mengubah apa pun dalam hal fungsionalitas, Derived.BasePropertymasih akan benar-benar kembali "BASE_AWESOME", dan dengan demikian ini bukan akar bug kami di tempat kerja, hanya komponen yang membingungkan. Namun saya merasa menarik bagaimana itu bisa menyesatkan para pengembang yang tidak menyadari fakta itu selama sesi debug mereka, khususnya jika Basetidak diekspos dalam proyek Anda tetapi lebih dirujuk sebagai DLL pihak ke-3, yang mengakibatkan Devs hanya mengatakan:

"Oi, tunggu..apa? Omg seperti DLL itu, ..melakukan sesuatu yang lucu"

Dynami Le Savard
sumber
Itu tidak ada yang istimewa, itu hanya cara menimpa bekerja.
konfigurator
7

Yang ini cukup sulit untuk teratas. Saya berlari ke dalamnya ketika saya mencoba untuk membangun implementasi RealProxy yang benar-benar mendukung Begin / EndInvoke (terima kasih MS karena membuat ini tidak mungkin dilakukan tanpa peretasan yang mengerikan). Contoh ini pada dasarnya adalah bug di CLR, jalur kode yang tidak dikelola untuk BeginInvoke tidak memvalidasi bahwa pesan kembali dari RealProxy.PrivateInvoke (dan penggantian Invoke saya) mengembalikan contoh IAsyncResult. Setelah dikembalikan, CLR menjadi sangat bingung dan kehilangan ide tentang apa yang terjadi, seperti yang ditunjukkan oleh tes di bagian bawah.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Reflection;
using System.Runtime.Remoting.Messaging;

namespace BrokenProxy
{
    class NotAnIAsyncResult
    {
        public string SomeProperty { get; set; }
    }

    class BrokenProxy : RealProxy
    {
        private void HackFlags()
        {
            var flagsField = typeof(RealProxy).GetField("_flags", BindingFlags.NonPublic | BindingFlags.Instance);
            int val = (int)flagsField.GetValue(this);
            val |= 1; // 1 = RemotingProxy, check out System.Runtime.Remoting.Proxies.RealProxyFlags
            flagsField.SetValue(this, val);
        }

        public BrokenProxy(Type t)
            : base(t)
        {
            HackFlags();
        }

        public override IMessage Invoke(IMessage msg)
        {
            var naiar = new NotAnIAsyncResult();
            naiar.SomeProperty = "o noes";
            return new ReturnMessage(naiar, null, 0, null, (IMethodCallMessage)msg);
        }
    }

    interface IRandomInterface
    {
        int DoSomething();
    }

    class Program
    {
        static void Main(string[] args)
        {
            BrokenProxy bp = new BrokenProxy(typeof(IRandomInterface));
            var instance = (IRandomInterface)bp.GetTransparentProxy();
            Func<int> doSomethingDelegate = instance.DoSomething;
            IAsyncResult notAnIAsyncResult = doSomethingDelegate.BeginInvoke(null, null);

            var interfaces = notAnIAsyncResult.GetType().GetInterfaces();
            Console.WriteLine(!interfaces.Any() ? "No interfaces on notAnIAsyncResult" : "Interfaces");
            Console.WriteLine(notAnIAsyncResult is IAsyncResult); // Should be false, is it?!
            Console.WriteLine(((NotAnIAsyncResult)notAnIAsyncResult).SomeProperty);
            Console.WriteLine(((IAsyncResult)notAnIAsyncResult).IsCompleted); // No way this works.
        }
    }
}

Keluaran:

No interfaces on notAnIAsyncResult
True
o noes

Unhandled Exception: System.EntryPointNotFoundException: Entry point was not found.
   at System.IAsyncResult.get_IsCompleted()
   at BrokenProxy.Program.Main(String[] args) 
Steve
sumber
6

Saya tidak yakin apakah Anda akan mengatakan ini adalah keanehan Windows Vista / 7 atau. Keanehan Net tetapi saya menggaruk-garuk kepala untuk sementara waktu.

string filename = @"c:\program files\my folder\test.txt";
System.IO.File.WriteAllText(filename, "Hello world.");
bool exists = System.IO.File.Exists(filename); // returns true;
string text = System.IO.File.ReadAllText(filename); // Returns "Hello world."

Pada Windows Vista / 7 file tersebut sebenarnya akan ditulis C:\Users\<username>\Virtual Store\Program Files\my folder\test.txt

Spencer Ruport
sumber
2
Ini memang peningkatan keamanan vista (bukan 7, afaik). Tapi yang keren adalah Anda bisa membaca dan membuka file dengan path file program, sedangkan jika Anda melihat di sana dengan explorer tidak ada apa-apa. Yang ini membawa saya hampir sehari kerja @ pelanggan sebelum akhirnya saya tahu.
Henri
Ini pasti Windows 7 juga. Itulah yang saya gunakan ketika saya menabraknya. Saya mengerti alasan di balik itu tetapi masih frustasi untuk mencari tahu.
Spencer Ruport
Di Vista / Win 7 (winXP secara teknis juga) aplikasi harus menulis ke folder AppData di tanah Users-folder, sebagai data pengguna secara teknis. Aplikasi tidak boleh menulis ke file program / windows / system32 / dll, kecuali jika mereka memiliki admin priveledges, dan mereka priveledges hanya boleh ada di sana untuk mengatakan meng-upgrade program / uninstall / instal fitur baru. TAPI! Masih tidak menulis ke system32 / windows / etc :) Jika Anda menjalankan kode di atas sebagai admin (klik kanan> run as admin) secara teoritis harus menulis ke folder aplikasi file program.
Steve Syfuhs
Kedengarannya seperti Virtualisasi - crispybit.spaces.live.com/blog/cns!1B71C2122AD43308!134.entry
Colin Newell
6

Pernahkah Anda berpikir bahwa kompiler C # dapat menghasilkan CIL yang tidak valid? Jalankan ini dan Anda akan mendapatkan TypeLoadException:

interface I<T> {
  T M(T p);
}
abstract class A<T> : I<T> {
  public abstract T M(T p);
}
abstract class B<T> : A<T>, I<int> {
  public override T M(T p) { return p; }
  public int M(int p) { return p * 2; }
}
class C : B<int> { }

class Program {
  static void Main(string[] args) {
    Console.WriteLine(new C().M(42));
  }
}

Saya tidak tahu bagaimana tarifnya di kompiler C # 4.0.

EDIT : ini adalah output dari sistem saya:

C:\Temp>type Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1 {

  interface I<T> {
    T M(T p);
  }
  abstract class A<T> : I<T> {
    public abstract T M(T p);
  }
  abstract class B<T> : A<T>, I<int> {
    public override T M(T p) { return p; }
    public int M(int p) { return p * 2; }
  }
  class C : B<int> { }

  class Program {
    static void Main(string[] args) {
      Console.WriteLine(new C().M(11));
    }
  }

}
C:\Temp>csc Program.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.1
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Temp>Program

Unhandled Exception: System.TypeLoadException: Could not load type 'ConsoleAppli
cation1.C' from assembly 'Program, Version=0.0.0.0, Culture=neutral, PublicKeyTo
ken=null'.
   at ConsoleApplication1.Program.Main(String[] args)

C:\Temp>peverify Program.exe

Microsoft (R) .NET Framework PE Verifier.  Version  3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

[token  0x02000005] Type load failed.
[IL]: Error: [C:\Temp\Program.exe : ConsoleApplication1.Program::Main][offset 0x
00000001] Unable to resolve token.
2 Error(s) Verifying Program.exe

C:\Temp>ver

Microsoft Windows XP [Version 5.1.2600]
Jordão
sumber
Bekerja untuk saya dengan kompiler C # 3.5 dan kompiler C # 4 ...
Jon Skeet
Dalam sistem saya, itu tidak berfungsi. Saya akan menempelkan output dalam pertanyaan.
Jordão
Gagal bagi saya di .NET 3.5 (tidak punya waktu untuk menguji 4.0). Dan saya bisa meniru masalah dengan kode VB.NET.
Mark Hurd
3

Ada sesuatu yang sangat menarik tentang C #, cara menangani penutupan.

Alih-alih menyalin nilai-nilai variabel tumpukan ke variabel bebas penutupan, ia melakukan sihir preprosesor yang membungkus semua kejadian variabel ke dalam objek dan dengan demikian memindahkannya dari tumpukan - langsung ke tumpukan! :)

Saya kira, itu membuat bahasa C # lebih lengkap secara fungsional (atau lambda-selesai ya)) daripada ML itu sendiri (yang menggunakan nilai tumpukan menyalin AFAIK). F # memiliki fitur itu juga, seperti halnya C #.

Itu membawa banyak kesenangan bagi saya, terima kasih MS guys!

Ini bukan keanehan atau kasus sudut ... tetapi sesuatu yang benar-benar tak terduga dari bahasa VM berbasis stack :)

Bubba88
sumber
3

Dari pertanyaan yang saya tanyakan belum lama ini:

Operator kondisional tidak dapat memberikan secara implisit?

Diberikan:

Bool aBoolValue;

Di mana aBoolValueditugaskan Benar atau Salah;

Berikut ini tidak akan dikompilasi:

Byte aByteValue = aBoolValue ? 1 : 0;

Tetapi ini akan:

Int anIntValue = aBoolValue ? 1 : 0;

Jawaban yang diberikan juga cukup bagus.

MPelletier
sumber
walaupun saya ve not test it Iyakin ini akan berhasil: Byte aByteValue = aBoolValue? (Byte) 1: (Byte) 0; Atau: Byte aByteValue = (Byte) (aBoolValue? 1: 0);
Alex Pacurar
2
Ya, Alex, itu akan berhasil. Kuncinya ada di casting implisit. 1 : 0sendiri secara implisit akan dilemparkan ke int, bukan Byte.
MPelletier
2

Pelingkupan dalam c # benar-benar aneh pada waktu-waktu tertentu. Izinkan saya memberi Anda satu contoh:

if (true)
{
   OleDbCommand command = SQLServer.CreateCommand();
}

OleDbCommand command = SQLServer.CreateCommand();

Ini gagal dikompilasi, karena perintah redeclared? Ada beberapa dugaan yang tertarik mengapa ini bekerja seperti itu di utas ini di stackoverflow dan di blog saya .

Anders Rune Jensen
sumber
34
Saya tidak melihat itu sebagai sangat aneh. Apa yang Anda sebut "kode yang benar-benar benar" di blog Anda benar- benar salah sesuai dengan spesifikasi bahasa. Mungkin benar di beberapa bahasa imajiner yang Anda inginkan C #, tetapi spesifikasi bahasa cukup jelas bahwa dalam C # itu tidak valid.
Jon Skeet
7
Yah itu valid dalam C / C ++. Dan karena itu C # saya ingin tetap bekerja. Yang paling mengganggu saya adalah bahwa tidak ada alasan bagi kompiler untuk melakukan ini. Tidak sulit melakukan pelingkupan bersarang. Saya kira semuanya bermuara pada unsur yang paling tidak mengejutkan. Berarti spek itu bisa mengatakan ini dan itu, tapi itu tidak banyak membantu saya jika benar-benar tidak masuk akal bahwa itu berperilaku seperti itu.
Anders Rune Jensen
6
C #! = C / C ++. Apakah Anda juga ingin menggunakan cout << "Hello World!" << endl; alih-alih Console.WriteLine ("Hello World!") ;? Juga tidak masuk akal, cukup baca speknya.
Kredns
9
Saya berbicara tentang aturan pelingkupan yang merupakan bagian dari inti bahasa. Anda berbicara tentang perpustakaan standar. Tapi sekarang jelas bagi saya bahwa saya harus membaca spesifikasi kecil bahasa c # sebelum saya mulai pemrograman di dalamnya.
Anders Rune Jensen
6
Eric Lippert sebenarnya memposting alasan mengapa C # dirancang seperti itu baru-baru ini: blogs.msdn.com/ericlippert/archive/2009/11/02/… . Ringkasan ini karena kecil kemungkinannya bahwa perubahan akan memiliki konsekuensi yang tidak diinginkan.
Helephant