Scriver numeri in lettere ROMANE in C#


Dop il ICustomFromatter per scrivere numeri in lettere, ecco un’altro FormatProvider per scrivere un numero intero da cifre arabe in cifre romane:

public class RomanFormatProvider : IFormatProvider, ICustomFormatter
    {

        IFormatProvider _parent;

        public RomanFormatProvider() : this(CultureInfo.CurrentCulture) { }
        public RomanFormatProvider(IFormatProvider parent)
        {
            _parent = parent;
        }

        public object GetFormat(Type formatType)
        {
            if (formatType == typeof(ICustomFormatter)) return this;
            return null;
        }

        public string Format(string format, object arg, IFormatProvider prov)
        {

            if (arg == null || format != "RO")
                return string.Format(_parent, "{0:" + format + "}", arg);

            return ToRoman(arg.ToString());
        }

        private string ToRoman(string n)
        {
            StringBuilder r = new StringBuilder();

            int i = 0;
            foreach (var c in n)
            {
                r.Append(calcDigit(int.Parse(c.ToString()), n.Length  - i - 1));
                i++;
            }
            return r.ToString();
        }

        class Level
        {
            public Level(string i, string v, string x)
            {
                this.i = i;
                this.v = v;
                this.x = x;
            }
            public string i { get; set; }
            public string v { get; set; }
            public string x { get; set; }

        }

        static Level[] levels = new RomanFormatProvider.Level[] {
            new Level("I", "V", "X"),
            new Level("X", "L", "C"),
            new Level("C", "D", "M")
        };

        string calcDigit(int d, int l)
        {
            string str;
            if (l > 2)
            {
                str = "";
                for (var k = 1; k <= d * Math.Pow (10, l - 3); k++)
                    str += "M";
                return str;
            }
            else
                switch (d)
                {
                    case 1:
                        return levels[l].i;
                         
                    case 2:
                        return levels[l].i + levels[l].i;
                         
                    case 3:
                        return levels[l].i + levels[l].i + levels[l].i;
                       
                    case 4:
                        return levels[l].i + levels[l].v;
                       
                    case 5:
                        return levels[l].v;
                       
                    case 6:
                        return levels[l].v + levels[l].i;
                       
                    case 7:
                        return levels[l].v + levels[l].i + levels[l].i;
                       
                    case 8:
                        return levels[l].v + levels[l].i + levels[l].i + levels[l].i;
                       
                    case 9:
                        return levels[l].i + levels[l].x;
                        
                    default:
                        return "";
                        
                }
        }
    }

Con questo CustomFormatter è possibile scrivere semplicemente:

RomanFormatProvider fr = new RomanFormatProvider();
s = string.Format(fr, "Il numero {0} in lettere è {0:RO}", 1971);
Console.WriteLine(s);

Il cui output è:

Il numero 1971 in lettere è MCMLXXI