StackOverflowException di setter dengan properti pendukung

PEMBARUAN: Saya telah memperbaiki masalah yang saya alami tetapi saya tidak tahu mengapa bug tersebut menghasilkan jejak tumpukan seperti itu. Jejak tumpukan membawa saya ke arah yang salah. Jika ada yang bisa menjelaskan apa yang terjadi di sini saya akan sangat menghargainya (dan akan menandai jawaban Anda sebagai diterima). Perhatikan bahwa postingan asli saya telah dihapus.

Saya mengikuti kelas berikut. Bagian yang tidak relevan telah dihapus:

class ClassName {
    private string[] _accountTypes = new string[2] {"ECOM", "MOTO"};

    private Dictionary<string, string> _settleDueDateDictionary = new Dictionary<string, string>() {
        {"0", "Process immediately."},
        {"1", "Wait 1 day"},
        {"2", "Wait 2 days"},
        {"3", "Wait 3 days"},
        {"4", "Wait 4 days"},
        {"5", "Wait 5 days"},
        {"6", "Wait 6 days"},
        {"7", "Wait 7 days"},
    };


    private string _settleDueDate;

    private string _accountTypeDescription;


    public string SettleDueDate
    {
        get
        {
            DateTime today = DateTime.Today;
            long settleDueDate = Convert.ToInt64(_settleDueDate);
            return today.AddDays(settleDueDate).ToString("MM/dd/yyyy");
        }
        set
        {   
            if (!_settleDueDateDictionary.ContainsKey(value)) {
                // TODO - handle
            }
            _settleDueDate = value;
        }
    }

    public string AccountTypeDescription
    {
        get {
            //return AccountTypeDescription; // This would cause infinite recursion (not referring to backing property).
            return _accountTypeDescription; // This fixed the StackOverflowException I was faxed with
        }
        set
        {
            if (!_accountTypes.Contains(value))
            {
                // TODO - handle
            }
            _accountTypeDescription = value;
        }
    }
}

Saya juga memiliki kelas ini yang mengambil instance dari kelas di atas dan membuat string XML menggunakan nilai dari instance:

class SecondClass
{
    private ClassName classnameInstance;

    public SecondClass(ClassName instance)
    {
        classnameInstance = instance;
    }

    public string PrepareRequest(XMLWriter writer)
    {
        writer.WriteElementString("accounttypedescription", classnameInstance.AccountTypeDescription);
    }
}

Berikut adalah kode klien yang menghasilkan pelacakan tumpukan:

STPPData STPP = new STPPData();

STPP.SiteReference = _secureTradingWebServicesPaymentSettings.SiteReference;
STPP.Alias = _secureTradingWebServicesPaymentSettings.Alias;

STPP.SettleDueDate = Convert.ToString(_secureTradingWebServicesPaymentSettings.SettleDueDate);
STPP.SettleStatus = _secureTradingWebServicesPaymentSettings.SettleStatus;
STPPXml STPPXml = new STPPXml(STPP);

XmlWriterSettings settings = new XmlWriterSettings();
settings.Async = false;
var builder = new StringBuilder();

using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
    string xmlRequest = STPPXml.PrepareRequest(writer);
}

Terakhir, inilah jejak tumpukannya:

mscorlib.dll!string.GetHashCode()
mscorlib.dll!System.Collections.Generic.GenericEqualityComparer<System.__Canon>.GetHashCode(SYstem.__Canon obj)
mscorlib.dll!System.Collections.Generic.Dictionary<string,string>.FindEntry(string key)
mscorlib.dll!System.Collections.Generic.Dictionary<System.__Canon,System.__Canon>.ContainsKey(System.__Canon key)
ClassName.SettleDueDate.set(string value)
ClassName.SettleDueDate.set(string value)
ClassName.SettleDueDate.set(string value)
// Infinite recursion of this call

Pelacakan tumpukan ini membuat saya yakin bahwa saya telah salah mengimplementasikan pengambil/penyetel untuk STPP.SettleDueDate. Saya memeriksanya dan variabel pendukung dll. sudah benar (penyebab umum loop di getter/setter, saya mengerti). Proses debug lebih lanjut menunjukkan kepada saya bahwa pelacakan tumpukan sebenarnya dihasilkan ketika baris PrepareRequest() ini dipanggil:

writer.WriteElementString("accounttypedescription", STPPData.AccountTypeDescription);

Saya menemukan bahwa saya salah menerapkan pengambil untuk STPPData.AccountTypeDescription karena saya telah membuat properti pendukung yang saya gunakan di penyetel tetapi saya TIDAK menggunakan properti pendukung di pengambil:

public string AccountTypeDescription
{
    get {
        //return AccountTypeDescription; // This would cause infinite recursion.
        return _accountTypeDescription; // This fixed the StackOverflowException
    }
    // setter omitted for clarity (it is in the examples above)
}

Pertanyaanku adalah:

Mengapa pelacakan tumpukan StackOverflowException mengarahkan saya ke SettleDueDate.set() padahal bug sebenarnya ada di dalam AccountTypeDescription.get()?

Catatan: Saya baru mengenal C# dan berasal dari latar belakang LAMP. Saya telah menyederhanakan kodenya sedikit tetapi saya rasa saya tidak menghapus sesuatu yang penting.


person pb149    schedule 07.12.2012    source sumber
comment
Tanpa mengetahui apa itu _settleDueDateDictionary (kode), mustahil untuk membantu.   -  person Erik Philips    schedule 07.12.2012
comment
+1 untuk StackOverflowException   -  person jenson-button-event    schedule 07.12.2012
comment
@Erik Philips - Oke - Saya sudah menambahkan ini ke pertanyaan.   -  person pb149    schedule 07.12.2012
comment
Bisakah Anda membuat program singkat namun lengkap yang menunjukkan masalahnya?   -  person Jon Skeet    schedule 07.12.2012
comment
Anda harus melihat jejak tumpukan pengecualian - ini akan menunjukkan rantai metode yang menghasilkan rekursi.   -  person Knaģis    schedule 07.12.2012
comment
@Knagis - Jika saya tidak mengatakannya, penyetel SettleDueDate adalah yang menghasilkan rekursi.   -  person pb149    schedule 07.12.2012
comment
mengapa Anda menyimpan nilai tanggal sebagai string, bukan sebagai DateTime?   -  person Servy    schedule 07.12.2012
comment
@ Pete171 - sepertinya hal itu tidak mungkin dilakukan dari kode yang Anda posting - tidak ada panggilan rekursif. Mungkin ada kode lain yang Anda lewati untuk memperkecil ukurannya di pertanyaan?   -  person Knaģis    schedule 07.12.2012
comment
Dari jejak tumpukan sepertinya harus ada SettleDueDate = value; di suatu tempat di setter (bukan _settleDueDate = value;), atau sesuatu seperti itu.   -  person rsbarro    schedule 07.12.2012
comment
@Knagis - Saya tidak tahu apa yang akan terjadi. Anda memiliki definisi lengkap tentang _settleDueDateDictionary pengambil dan penyetel SettleDueDate serta variabel pendukung _settleDueDate pribadi. Satu-satunya hal yang saya ubah adalah bagaimana anIntParameterHere diteruskan ke penyetel. Menurutku bukan itu masalahnya? Apa lagi yang bisa saya berikan? ClassName jika tidak, hanya berisi pengambil/penyetel lain dan beberapa anggota data lainnya.   -  person pb149    schedule 07.12.2012
comment
@Servy - Mungkin karena saya baru mengenal C#! Tapi setelah membentuk string itu kita perlu menginterpolasinya ke dalam XML menggunakan XMLWriter, jadi menurut saya itu mungkin boleh digunakan?   -  person pb149    schedule 07.12.2012
comment
@leppie - Seperti di atas, baru mengenal C# jadi mungkin melakukan beberapa hal aneh... Saya akan melihatnya setelah ini diselesaikan.   -  person pb149    schedule 07.12.2012
comment
@ Pete171 Anda harus menyimpannya sebagai DateTime di seluruh aplikasi Anda dan kemudian mengonversinya menjadi string sebelum Anda menyimpannya dalam file XML, daripada melakukan operasi pada file tersebut di seluruh aplikasi Anda sebagai string.   -  person Servy    schedule 07.12.2012
comment
@Knagis - Saya melakukan debug lagi dan menemukan bahwa kode di atas bukanlah bagian yang bermasalah. Permintaan maaf karena tidak memberikan sisanya - benar-benar tidak melihat bagaimana apa yang saya lewatkan bisa menjadi masalahnya.   -  person pb149    schedule 07.12.2012
comment
Semua: Saya telah memperbaiki masalahnya. Tidak tahu mengapa StackOverflowException mengatakan hal itu. Masalahnya ada di tempat lain. Pertanyaan ditulis ulang untuk menanyakan mengapa ini terjadi.   -  person pb149    schedule 10.12.2012
comment
@ Pete171 - Di mana kode yang menyetel SettleDueDate? Apakah mungkin Anda meneruskan nilai AccountTypeDescription ke dalamnya dan/atau menggunakannya sebagai kunci kamus?   -  person Bobson    schedule 10.12.2012
comment
@Bobson - Silakan lihat blok kode ketiga (kode klien saya). Salah satu baris di sana (dimulai STPP.SettleDueDate = Convert.To...) adalah bagian yang menyetel SettleDueDate. Terima kasih.   -  person pb149    schedule 10.12.2012
comment
@ Pete171 - Ah, saya melewatkannya, terima kasih. Saya tidak punya sesuatu yang berguna, maaf.   -  person Bobson    schedule 10.12.2012
comment
@ Pete171 - Saya baru saja berpikir. Bisakah Anda menyalin sebagian kecil kode Anda dan mereproduksinya? Dan/atau bisakah Anda mengunggah zip seluruh direktori di suatu tempat agar orang lain dapat mencoba dan mereproduksinya?   -  person Bobson    schedule 12.12.2012


Jawaban (2)


Berikut adalah beberapa langkah proses debug sederhana yang akan mempersempit masalah

  • Buka kelas dengan properti SettleDueDate
  • Klik kanan pada nama properti untuk SettleDueDate
  • Klik pada item menu 'Temukan semua referensi'
  • Setiap tempat di mana SettleDueDate disetel yaitu 'SettleDueDate = "Sesuatu atau lainnya"' menambahkan breakpoint
  • Jalankan aplikasi dan terus lanjutkan ketika breakpoint tercapai hingga ada yang terkena beberapa kali berturut-turut
  • Ketika Anda menemukan titik yang menyinggung dan kode berada pada breakpoint alih-alih melanjutkan, gunakan perintah Step Out dan perintah Step over untuk menelusuri jalan kembali ke atas tumpukan untuk mencari tahu di mana kode tersebut ditugaskan secara rekursif
person Bryan Roberts    schedule 07.12.2012
comment
Terima kasih atas jawabannya, tetapi saya telah memasukkan beberapa breakpoint setiap kali SettleDueDate direferensikan tetapi tidak ada satu pun breakpoint yang tercapai! - person pb149; 08.12.2012

Kode ini sangat terpisah-pisah dan saya tidak yakin saya cukup memahami semua hubungannya. Saya berasumsi bahwa ClassName == STPPData dan SecondClass == STPPXml? Meskipun demikian, saya mencoba mereproduksi bug ini menggunakan VS2010 dan .NET 4. Saya tidak dapat melakukannya - pelacakan tumpukan menunjukkan rekursi tak terbatas hanya dalam AccountTypeDescription.set(). Pasti ada sesuatu yang hilang.

Pertama-tama, baris-baris dalam pelacakan tumpukan ini sangat menarik:

mscorlib.dll!string.GetHashCode()
mscorlib.dll!System.Collections.Generic.GenericEqualityComparer<System.__Canon>.GetHashCode(SYstem.__Canon obj)
mscorlib.dll!System.Collections.Generic.Dictionary<string,string>.FindEntry(string key)
mscorlib.dll!System.Collections.Generic.Dictionary<System.__Canon,System.__Canon>.ContainsKey(System.__Canon key)

Mereka tampaknya secara pasti menunjukkan bagian dalam SettleDueDate.set(), bukan hanya panggilan tak terbatas ke sana. Kamus dan pencarian hash ada. Anda pasti mempunyai bug di suatu tempat di sana. Namun saya punya firasat bahwa kode sumber Anda tidak mengandung bug. Mengacu pada jawaban @ Bryan, apakah Anda menyetel breakpoint di dalam penyetel SettleDueDate dan bukan di tempat dalam kode tempat Anda menyebutnya? Jika Anda menggunakan visual studio, Anda juga dapat menghentikan pengecualian menggunakan fitur ini.

Saya melihat Anda melakukan sesuatu dengan layanan web dan itu langsung membuat saya berpikir tentang kelas proxy. Mereka mungkin terlihat sangat mirip dengan kode yang Anda tulis, tetapi itu bukanlah kode yang Anda tulis. Mereka bisa menjadi basi. Seberapa yakin Anda bahwa pengecualian ini disebabkan oleh kode yang Anda tulis dan kompilasi?

Kedua, layanan web juga membuat saya berpikir tentang serialisasi, sebuah proses yang sering kali memanggil pengambil dan penyetel properti tanpa sepengetahuan Anda. Saya pernah mengalami masalah di masa lalu dengan WCF yang mencoba membuat serialisasi IEnumerables dan gagal total meskipun kode saya baik-baik saja.

Selain itu, di sistem saya, baris ini tidak dapat dikompilasi:

if (!_accountTypes.Contains(value))

Ini membuat saya bertanya-tanya apakah Anda menggunakan Mono atau IDE yang berbeda dari saya. Lagipula kamu adalah orang LAMP =)

Saya tahu (belum) ini bukan jawaban yang sebenarnya, tapi saya ingin tahu apa pendapat Anda tentang ini? Adakah detail lain yang dapat Anda bagikan?

person killthrush    schedule 08.03.2013