StackOverflowException ใน setter พร้อมคุณสมบัติสำรอง

อัปเดต: ฉันได้แก้ไขปัญหาที่ฉันพบแล้ว แต่ฉันไม่รู้ว่าทำไมจุดบกพร่องจึงสร้างการติดตามสแต็กอย่างที่มันทำ การติดตามสแต็กทำให้ฉันไปในทิศทางที่ผิดอย่างสิ้นเชิง หากใครสามารถอธิบายสิ่งที่เกิดขึ้นที่นี่ ฉันจะขอบคุณ (และจะทำเครื่องหมายคำตอบของคุณว่ายอมรับ) โปรดทราบว่าโพสต์ต้นฉบับของฉันถูกลบแล้ว

ฉันมีชั้นเรียนต่อไปนี้ ส่วนที่ไม่เกี่ยวข้องได้ถูกลบออกแล้ว:

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

ฉันยังมีคลาสนี้ซึ่งรับอินสแตนซ์ของคลาสด้านบนและสร้างสตริง XML โดยใช้ค่าจากอินสแตนซ์:

class SecondClass
{
    private ClassName classnameInstance;

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

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

นี่คือรหัสไคลเอ็นต์ที่สร้างการติดตามสแต็ก:

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

สุดท้าย นี่คือการติดตามสแต็ก:

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

การติดตามสแต็กนี้ทำให้ฉันเชื่อว่าฉันใช้ getter/setter สำหรับ STPP.SettleDueDate อย่างไม่ถูกต้อง ฉันตรวจสอบแล้วและตัวแปรสำรอง ฯลฯ ถูกต้อง (ฉันเข้าใจสาเหตุปกติของการวนซ้ำใน getters/setters) การดีบักเพิ่มเติมแสดงให้ฉันเห็นว่าการติดตามสแต็กถูกสร้างขึ้นจริงเมื่อมีการเรียกใช้บรรทัด PrepareRequest() นี้:

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

ฉันค้นพบว่าฉันใช้ getter สำหรับ STPPData.AccountTypeDescription ไม่ถูกต้อง เนื่องจากฉันได้สร้างคุณสมบัติการสำรองข้อมูลที่ฉันใช้ใน setter แต่ฉันไม่ได้ใช้คุณสมบัติการสนับสนุนใน getter:

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

คำถามของฉันคือ:

เหตุใดการติดตามสแต็กของ StackOverflowException จึงชี้ให้ฉันไปที่ SettleDueDate.set() ทั้งที่จริงๆ แล้วจุดบกพร่องนั้นอยู่ภายใน AccountTypeDescription.get()

หมายเหตุ: ฉันยังใหม่กับ C# และมาจากพื้นหลัง LAMP ฉันทำให้โค้ดง่ายขึ้นเล็กน้อย แต่ฉันไม่คิดว่าจะลบสิ่งที่สำคัญออกไป


person pb149    schedule 07.12.2012    source แหล่งที่มา
comment
โดยไม่รู้ว่า _settleDueDateDictionary คืออะไร (รหัส) ก็แทบจะเป็นไปไม่ได้เลยที่จะช่วย   -  person Erik Philips    schedule 07.12.2012
comment
+1 สำหรับ StackOverflowException   -  person jenson-button-event    schedule 07.12.2012
comment
@Erik Philips - ตกลง - ฉันได้เพิ่มสิ่งนี้ลงในคำถามแล้ว   -  person pb149    schedule 07.12.2012
comment
คุณสามารถสร้างโปรแกรมสั้นๆ แต่ สมบูรณ์ ที่แสดงปัญหาได้หรือไม่   -  person Jon Skeet    schedule 07.12.2012
comment
คุณควรดูที่การติดตามสแต็กข้อยกเว้น ซึ่งจะแสดงห่วงโซ่วิธีการที่ส่งผลให้เกิดการเรียกซ้ำ   -  person Knaģis    schedule 07.12.2012
comment
@Knagis - ถ้าฉันไม่ได้พูด setter ของ SettleDueDate คือคนที่ทำให้เกิดการเรียกซ้ำ   -  person pb149    schedule 07.12.2012
comment
เหตุใดคุณจึงเก็บค่าวันที่เป็นสตริงแทนที่จะเป็น DateTime   -  person Servy    schedule 07.12.2012
comment
@ Pete171 - ดูเหมือนจะเป็นไปไม่ได้จากรหัสที่คุณโพสต์ - ไม่มีการเรียกซ้ำ อาจมีรหัสอื่นที่คุณข้ามเพื่อลดขนาดในคำถาม   -  person Knaģis    schedule 07.12.2012
comment
จากการติดตามสแต็ก ดูเหมือนว่าจะต้องมี SettleDueDate = value; อยู่ที่ไหนสักแห่งในตัวตั้งค่า (แทนที่จะเป็น _settleDueDate = value;) หรืออะไรทำนองนั้น   -  person rsbarro    schedule 07.12.2012
comment
@Knagis - ฉันไม่รู้ว่ามันจะเป็นอย่างไร คุณมีคำจำกัดความแบบเต็มของ _settleDueDateDictionary the SettleDueDate getter and setter และตัวแปรสำรอง _settleDueDate ส่วนตัว สิ่งเดียวที่ฉันเปลี่ยนแปลงคือวิธีการส่ง anIntParameterHere ไปยังผู้ตั้งค่า ฉันไม่คิดว่านั่นอาจเป็นปัญหา? ฉันจะให้อะไรอีกบ้าง? ClassName มิฉะนั้นจะมีเพียง getters/setters อื่นๆ และสมาชิกข้อมูลอื่นๆ สองสามราย   -  person pb149    schedule 07.12.2012
comment
@Servy - อาจเป็นเพราะฉันยังใหม่กับ C #! แต่หลังจากสร้างสตริงนั้นแล้ว เราจำเป็นต้องสอดแทรกมันลงใน XML โดยใช้ XMLWriter ดังนั้นฉันคิดว่านั่นอาจใช้ได้ไหม   -  person pb149    schedule 07.12.2012
comment
@leppie - ดังที่กล่าวมาข้างต้น ยังใหม่กับ C# ดังนั้นอาจกำลังทำสิ่งแปลก ๆ เล็กน้อย ... ฉันจะดูสิ่งนั้นเมื่อได้รับการแก้ไขแล้ว   -  person pb149    schedule 07.12.2012
comment
@ Pete171 คุณควรจัดเก็บมันเป็น DateTime ทั่วทั้งแอปพลิเคชันของคุณ จากนั้นแปลงเป็นสตริงก่อนที่คุณจะจัดเก็บไว้ในไฟล์ XML แทนที่จะดำเนินการกับมันทั่วทั้งแอปพลิเคชันของคุณเป็นสตริง   -  person Servy    schedule 07.12.2012
comment
@Knagis - ฉันทำการดีบั๊กเพิ่มเติมและพบว่าโค้ดด้านบนไม่ใช่บิตที่เป็นปัญหา ขออภัยที่ไม่ได้ให้ส่วนที่เหลือ - ไม่เห็นว่าสิ่งที่ฉันพลาดไปอาจเป็นปัญหาได้อย่างไร   -  person pb149    schedule 07.12.2012
comment
ทั้งหมด: ฉันได้แก้ไขปัญหาแล้ว ไม่รู้ว่าทำไม StackOverflowException ถึงบอกว่ามันทำอะไร ปัญหาอยู่ที่อื่น คำถามถูกเขียนใหม่เพื่อถามว่าทำไมสิ่งนี้จึงเกิดขึ้น   -  person pb149    schedule 10.12.2012
comment
@ Pete171 - รหัสที่ตั้ง SettleDueDate อยู่ที่ไหน? เป็นไปได้ไหมที่คุณส่งค่า AccountTypeDescription เข้าไปและ/หรือใช้เป็นคีย์พจนานุกรม   -  person Bobson    schedule 10.12.2012
comment
@Bobson - โปรดดูบล็อกโค้ดที่สาม (รหัสลูกค้าของฉัน) บรรทัดหนึ่งในนั้น (เริ่มต้น STPP.SettleDueDate = Convert.To...) คือส่วนที่ตั้งค่า SettleDueDate ขอบคุณ.   -  person pb149    schedule 10.12.2012
comment
@ Pete171 - อ่า ฉันพลาดไปแล้ว ขอบคุณ ฉันไม่มีประโยชน์อะไรเลย ขออภัย   -  person Bobson    schedule 10.12.2012
comment
@ Pete171 - ฉันเพิ่งมีความคิด คุณสามารถคัดลอกส่วนเล็กๆ ของโค้ดของคุณแล้วทำซ้ำได้หรือไม่ และ/หรือคุณสามารถอัปโหลด zip ของไดเร็กทอรีทั้งหมดไว้ที่อื่นเพื่อให้คนอื่นลองทำซ้ำได้หรือไม่?   -  person Bobson    schedule 12.12.2012


คำตอบ (2)


ด้านล่างนี้เป็นขั้นตอนการแก้ไขจุดบกพร่องง่ายๆ ที่ควรจำกัดปัญหาให้แคบลง

  • เปิดชั้นเรียนด้วยคุณสมบัติ SettleDueDate
  • คลิกขวาที่ชื่อคุณสมบัติสำหรับ SettleDueDate
  • คลิกที่รายการเมนู 'ค้นหาข้อมูลอ้างอิงทั้งหมด'
  • ทุกสถานที่ที่ตั้งค่า SettleDueDate เช่น 'SettleDueDate = "บางอย่างหรืออื่น ๆ"' เพิ่มเบรกพอยต์
  • เรียกใช้แอปพลิเคชันและดำเนินการต่อเมื่อเบรกพอยต์ถูกโจมตีจนกว่าจะโดนโจมตีหลายครั้งติดต่อกัน
  • เมื่อคุณพบจุดที่กระทำผิดและโค้ดอยู่บนเบรกพอยต์แทนที่จะดำเนินการต่อ ให้ใช้คำสั่ง Step Out และคำสั่ง Step over เพื่อติดตามทางของคุณสำรองสแต็กเพื่อค้นหาว่าถูกกำหนดซ้ำไว้ที่ใด
person Bryan Roberts    schedule 07.12.2012
comment
ขอบคุณสำหรับคำตอบ แต่ฉันได้ใส่เบรกพอยต์ทุกครั้งที่มีการอ้างอิง SettleDueDate แต่ไม่มีถึงเบรกพอยต์เลยด้วยซ้ำ! - person pb149; 08.12.2012

รหัสนี้ไม่เป็นชิ้นเป็นอันมาก และฉันไม่แน่ใจว่าฉันค่อนข้างเข้าใจการเชื่อมต่อทั้งหมด ฉันสมมติว่า ClassName == STPPData และ SecondClass == STPPXml? อย่างไรก็ตาม ฉันพยายามจำลองจุดบกพร่องนี้โดยใช้ VS2010 และ .NET 4 แต่ทำไม่สำเร็จ - การติดตามสแต็กแสดงการเรียกซ้ำแบบไม่สิ้นสุดภายใน AccountTypeDescription.set() เท่านั้น ต้องมีบางอย่างขาดหายไป

ก่อนอื่น บรรทัดเหล่านี้ในการติดตามสแต็กมีความน่าสนใจมาก:

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)

ดูเหมือนว่าพวกมันจะแสดงอวัยวะภายในของ SettleDueDate.set() อย่างชัดเจน ไม่ใช่แค่การเรียกมันอย่างไม่มีที่สิ้นสุด มีการค้นหาพจนานุกรมและแฮช คุณมีข้อบกพร่องที่ไหนสักแห่งอยู่ในนั้นอย่างแน่นอน อย่างไรก็ตาม ฉันสังหรณ์ใจว่าซอร์สโค้ดของคุณไม่มีจุดบกพร่อง จากคำตอบของ @ Bryan คุณได้ตั้งค่าเบรกพอยต์ของคุณ ภายใน ตัวตั้งค่า SettleDueDate ซึ่งตรงข้ามกับตำแหน่งในโค้ดของคุณที่คุณเรียกมันหรือไม่ หากคุณใช้ Visual Studio คุณสามารถแยกข้อยกเว้นได้โดยใช้คุณลักษณะนี้.

ฉันเห็นว่าคุณกำลังทำสิ่งต่าง ๆ กับบริการบนเว็บ และนั่นทำให้ฉันนึกถึงคลาสพร็อกซีทันที พวกมันอาจดูเหมือนโค้ดที่คุณเขียนอย่างมาก แต่มันไม่ใช่โค้ดที่คุณเขียน พวกเขาสามารถเหม็นอับได้ คุณแน่ใจได้อย่างไรว่าข้อยกเว้นนี้เกิดจากโค้ดที่คุณเขียนและคอมไพล์?

ประการที่สอง บริการบนเว็บยังทำให้ฉันคิดถึงการทำให้เป็นอนุกรม ซึ่งเป็นกระบวนการที่มักจะเรียกตัวรับและผู้ตั้งค่าคุณสมบัติโดยที่คุณไม่รู้ ในอดีตฉันเคยมีปัญหากับ WCF ที่พยายามทำให้ IEnumerables เป็นอนุกรมและล้มเหลวอย่างน่ากลัวแม้ว่าโค้ด ของฉัน จะปกติดีก็ตาม

นอกจากนี้ ในระบบของฉันบรรทัดนี้ไม่ได้คอมไพล์:

if (!_accountTypes.Contains(value))

สิ่งนี้ทำให้ฉันสงสัยว่าคุณใช้ Mono หรือ IDE ที่แตกต่างจากฉันหรือไม่ คุณเป็นผู้ชาย LAMP เลย =)

ฉันรู้ว่านี่ไม่ใช่คำตอบที่แท้จริง (ยัง) แต่ฉันสงสัยว่าคุณทำอะไรกับสิ่งนี้ รายละเอียดอื่น ๆ ที่คุณสามารถแบ่งปันได้หรือไม่?

person killthrush    schedule 08.03.2013