Search code examples
c#stack-overflow

Calling a function on the set method of a property in C# throws System.StackOverflowException


New to c#. Here is the sample of the json I am working with. The objective is to have the two letter flag code converted to a flag emoji.

[
    {
      "name": "American Samoa",
      "flag": "🇦🇸",
      "code": "AS",
      "dialcode": "+1684"
    },
    {
      "name": "Andorra",
      "flag": "🇦🇩",
      "code": "AD",
      "dialcode": "+376"
    },
]

I have this code below which converts the two letter country code from a json file to a flag emoji. I am calling a method which actually does the conversion on the set method of the Flag property. Running this code throws a System.StackOverflowException.

using Newtonsoft.Json;
    
namespace Bol.Models
{
    internal class Countries
    {
        public List<Country> CountriesInfo { get; set; }
        public Countries()
        {
            StreamReader r = new StreamReader("C:\\Users\\folder\\Desktop\\Bol\\Bol\\Resources\\Data\\Countries.json");
            string jsonString = r.ReadToEnd();
            CountriesInfo = JsonConvert.DeserializeObject<List<Country>>(jsonString);           
        }
        
    }

    internal class Country
    {
        public string Name { get; set; }
        public string Flag { get { return Flag; } set { Flag = IsoCountryCodeToFlagEmoji(value); } }
        public string Code { get; set; }
        public string Dialcode { get; set; }

        public string IsoCountryCodeToFlagEmoji(string countryCode) => string.Concat(countryCode.ToUpper().Select(x => char.ConvertFromUtf32(x + 0x1F1A5)));
    }  
}

Solution

  • public string Flag
    {
        get { return Flag; }
        set { Flag = IsoCountryCodeToFlagEmoji(value); }
    }
    

    Let's look at your code here.

    • get: The call to get the value of Flag will have to call to get the value of the Flag property, which will call to get the value of the Flag property in an infinite loop.
    • set: The call to set the value of Flag will try and assign a value to the Flag property, which will try to assign a value to the Flag property in an infinite loop.

    You need a backing field for this.

    private string _flag;
    
    public string Flag
    {
        get { return _flag; }
        set { _flag = IsoCountryCodeToFlagEmoji(value); }
    }
    

    I'd suggest perhaps having two properties though, because the Flag property is currently transforming the value you input, so that what you get is not what you set. You could have one for country code, and one for flag:

    private string _countryCode;
    
    public string CountryCode
    {
        get { return _countryCode; }
        set
        {
            _countryCode = value;
            Flag = IsoCountryCodeToFlagEmoji(value);
        }
    }
    
    public string Flag { get; private set; }
    

    This calculates Flag at the time CountryCode is set. Alternatively, you could calculate it every time Flag is accessed:

    public string CountryCode { get; set; }
    public string Flag { get { return IsoCountryCodeToFlagEmoji(CountryCode); } }
    // or public string Flag => IsoCountryCodeToFlagEmoji(CountryCode);
    

    Re your question "isn't what what I'm doing with the other properties as well?": no it isn't.

    When compiled, public string Name { get; set; } becomes something like this:

    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string <Name>k__BackingField;
    
    public string Name
    {
        [CompilerGenerated]
        get
        {
            return <Name>k__BackingField;
        }
        [CompilerGenerated]
        set
        {
            <Name>k__BackingField = value;
        }
    }
    

    Notice how it has a separate backing field? Now let's compare that to what your Flag property gets compiled as:

    public string Flag
    {
        get
        {
            return Flag;
        }
        set
        {
            Flag = IsoCountryCodeToFlagEmoji(value);
        }
    }
    

    Notice how there's no backing field?

    You can see the compiled (and then decompiled, since C# is compiled to intermediate language) version here

    Note that member names with < and > are valid in IL but not in C#, so the compiler's code generation uses these to avoid conflicts with code you write.