UPDATE: I have fixed the problem I was experiencing but I don't know why the bug generated the stack trace that it did. The stack trace lead me in the completely wrong direction. If anyone can explain what was happening here I would appreciate it (and will mark your answer as accepted). Note that my original post has been deleted.
I had the following class. Non-relevant parts of it have been removed:
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;
}
}
}
I also had this class which took an instance of the class above and created an XML string using values from the instance:
class SecondClass
{
private ClassName classnameInstance;
public SecondClass(ClassName instance)
{
classnameInstance = instance;
}
public string PrepareRequest(XMLWriter writer)
{
writer.WriteElementString("accounttypedescription", classnameInstance.AccountTypeDescription);
}
}
Here is the client code that generated the stack trace:
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);
}
Finally, here is the stack trace:
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
This stack trace lead me to believe that I had incorrectly implemented the getter/setter for STPP.SettleDueDate. I checked them and the backing variable etc. was correct (the usual causes for loops in getters/setters, I understand). Further debugging showed me that the stack trace was actually generated when this line of PrepareRequest()
was called:
writer.WriteElementString("accounttypedescription", STPPData.AccountTypeDescription);
I discovered that I had incorrectly implemented the getter for STPPData.AccountTypeDescription because I had created a backing property that I used in the setter but I was NOT using the backing property in the 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)
}
My question is:
Why did the stack trace of the StackOverflowException point me to SettleDueDate.set() when the bug was actually inside AccountTypeDescription.get()?
Note: I'm new to C# and am coming from a LAMP background. I have simplified the code a little but I do not think I have removed anything important.
This code is very fragmentary and I'm not sure I quite understand all the connections. I'm assuming that ClassName == STPPData and SecondClass == STPPXml? Nonetheless, I tried reproducing this bug using VS2010 and .NET 4. I was unable to - the stack trace showed infinite recursion only within AccountTypeDescription.set(). There has to be something missing.
First of all, these lines in the stack trace are very interesting:
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)
They appear to definitively show the innards of SettleDueDate.set(), not just infinite calls to it. The dictionary and hash lookup are present. You definitely have a bug somewhere in there. However I have a hunch that your source code does not contain the bug. In reference to @Bryan's answer, did you set your breakpoints inside the SettleDueDate setter as opposed to places in your code where you call it? If you're using visual studio, you can also break on exceptions using this feature.
I saw that you are doing stuff with web services and that immediately makes me think of proxy classes. They can look an awful lot like code you write, but it isn't code you write. They can go stale. How sure are you that this exception was thrown by code you wrote and compiled?
Secondly, web services also make me think of serialization, a process that will often call property getters and setters without your knowledge. I've had issues in the past with WCF trying to serialize IEnumerables and failing horribly even though my code was just fine.
As an aside, on my system this line did not compile:
if (!_accountTypes.Contains(value))
This made me wonder if you are using Mono or a different IDE than me. You are a LAMP guy after all =)
I know this isn't a real answer (yet), but I'm wondering what you make of this? Any other details you can share?
Below are some simple debugging steps that should narrow down the problem
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With