1

Is there a way to make the default Enum.ToString() to convert to snake_case instead of PascalCase? And that change to be global, so I don't have to do that all over again.

public enum SpellTypes
{
    HorizonFocus
}

public sealed class Settings
{
    public Settings(SpellTypes types)
    {
        TypeString = types.ToString(); // actual: HorizonFocus, expected: horizon_focus
    }

    public string TypeString { get; }
}

In addition

I tried the following with Macross.Json.Extensions but it didn't apply the changes to the TypeString.

[JsonConverter(typeof(JsonStringEnumMemberConverter))]
public enum SpellTypes
{
    [EnumMember(Value = "horizon_focus")]
    HorizonFocus
}
nop
  • 4,711
  • 6
  • 32
  • 93
  • Why not use `[EnumMember( Value = "snake_case_value" )]` to set string values for enum members? – Dai Oct 24 '22 at 05:34
  • @Dai doesn't work with System.Text.Json. – nop Oct 24 '22 at 05:35
  • You didn't say you wanted `System.Text.Json` compatibility in your post. You should add that detail. (and `EnumMember` _does_ work with `Newtonsoft.Json`, at least). If so, then your answer is here: https://stackoverflow.com/questions/59059989/system-text-json-how-do-i-specify-a-custom-name-for-an-enum-value – Dai Oct 24 '22 at 05:36
  • `[JsonConverter(typeof(JsonStringEnumMemberConverter))]` will only affect the output of `System.Text.Json`, it won't affect `.ToString()` at all. In fact, you can't override `.ToString()` anyway: using `.ToString()` will always give you a string that matches the identifier of that enum value. – Dai Oct 24 '22 at 05:45
  • You can't have snake case if string serialization is not available in first place, the answer is here: [System.Text.Json: How do I specify a custom name for an enum value?](https://stackoverflow.com/questions/59059989/system-text-json-how-do-i-specify-a-custom-name-for-an-enum-value) – Orace Oct 24 '22 at 05:50
  • The obvious way to get snake case out is to rename the member. At that point, I'd hope you'd stop and ask yourself *why you instead expect the code to lie* about the name of the member? – Damien_The_Unbeliever Oct 24 '22 at 05:52
  • 1
    @Damien_The_Unbeliever _"I'd hope you'd stop and ask yourself..."_ - ...because for 5-minute one-off project, such as an ETL loader or data-export task, renaming an enum member is **a lot less work** than trying to figure out how to use `System.Text.Json` with custom `enum` values. – Dai Oct 24 '22 at 06:01

2 Answers2

2

You could create an extension method for all enums.

You can cache the snake case names in a dictionary in a generic helper class, for performance.

public static string To_snake_case<T>(this T value) where T : Enum
{
    return SnakeCaseHelper.Values.TryGetValue(value, out var name) ? name : null;
}

private static class SnakeCaseHelper<T> where T : Enum
{
    public static Dictionary<T, string> Values = new();

    static SnakeCaseHelper()
    {
        var names = Enum.GetNames(typeof(T));
        var values = (T[])Enum.GetValues(typeof(T));
        for (var i = 0; i < values.Length; i++)
            Values[values[i]] = GetSnakeCase(names[1]);
    }

    static string GetSnakeCase(string text)
    {
        if(text.Length < 2)
            return text;

        var sb = new StringBuilder();
        sb.Append(char.ToLowerInvariant(text[0]));
        for(int i = 1; i < text.Length; ++i)
        {
            char c = text[i];
            if(char.IsUpper(c))
            {
                sb.Append('_');
                sb.Append(char.ToLowerInvariant(c));
            }
            else
            {
                sb.Append(c);
            }
        }
        return sb.ToString();
    }
}

dotnetfiddle

Snake case function is from this answer.

Charlieface
  • 52,284
  • 6
  • 19
  • 43
  • Is there a reason you're using `static Dictionary` instead of `static readonly IReadOnlyDictionary`? – Dai Oct 24 '22 at 12:59
  • Because it's a private class and I couldn't be bothered thinking about that. Plus explicit class types have a very slight performance boost compared to interfaces. – Charlieface Oct 24 '22 at 13:01
1

you can use readonly type instead of enum

public class SpellTypes
    {
    public static readonly SpellTypes HorizonFocus = new SpellTypes( 1, "Horizon_Focus" );
    public static readonly SpellTypes HorizonFocus2 = new SpellTypes( 2, "Horizon_Focus2" );
    public static readonly SpellTypes HorizonFocus3 = new SpellTypes( 3, "Horizon_Focus3" );
    public readonly int num;
    public readonly string name;

    private SpellTypes( int num, String name )
    {
        this.num = num;
        this.name = name;
    }
}

public sealed class Settings
{
    public Settings( SpellTypes types )
    {
        TypeString = types.name.ToString(); 
    }

    public string TypeString { get; }
}
mkmkmk
  • 167
  • 4