¿Por qué los String son Inmutables? El String Intern Pool

En la programación es común que se nos indique que todas las variables de tipo String son inmutables, pero pocas veces se nos explica el ¿Por qué? de esto.

En la programación es común que se nos indique que todas las variables de tipo String son inmutables, pero pocas veces se nos explica el ¿Por qué? de esto.

Desde los inicios de la creación de los lenguajes de programación de alto nivel, es decir, en los años 50s, se detecto la gran problemática que traería la comparativa de variables de tipo cadena, ya que para comparar si 2 String son iguales, se necesitaba de algoritmos de O(n), es decir, algoritmos que tardarían dependiendo el numero de caracteres que tuviera tu variable de tipo String.

A parte, cuando se guarda una variable String, esta se distribuye por la memoria en distintas direcciones, por lo cual, se agrega un nivel extra de rendimiento.

En los años 60s se opto por utilizar un mecanismo el cual guardará los String distintos y solo asignarle la dirección de memoria a la variable de este tipo, de esta manera, al comparar 2 String, solo se compararía si correspondían a la misma dirección de memoria o no. Este mecanismo es llamado String Intern Pool.

El String Intern Pool es utilizado por la mayoría de lenguajes de programación más utilizados, como es el caso de C#, Python, Java y PHP.

A continuación te explico más a fondo de que va este concepto:

Clase en C# .Net para convertir una cantidad monetaria en letra.

Comparto una clase que sirve para convertir un número decimal a una cantidad en letra.

Es decir si queremos convertir el numero 50.56 nos daría como resultado “Cincuenta pesos 56/100 M.N”.

La clase está diseñada para mostrar cantidades monetarias en dólares, euros y pesos mexicanos, pero no es difícil modificar para la moneda que se desee.


public class Moneda
    {
        private String[] UNIDADES = { "", "un ", "dos ", "tres ", "cuatro ", "cinco ", "seis ", "siete ", "ocho ", "nueve " };
        private String[] DECENAS = {"diez ", "once ", "doce ", "trece ", "catorce ", "quince ", "dieciseis ",
        "diecisiete ", "dieciocho ", "diecinueve", "veinte ", "treinta ", "cuarenta ",
        "cincuenta ", "sesenta ", "setenta ", "ochenta ", "noventa "};
        private String[] CENTENAS = {"", "ciento ", "doscientos ", "trecientos ", "cuatrocientos ", "quinientos ", "seiscientos ",
        "setecientos ", "ochocientos ", "novecientos "};

        private Regex r;

        public String Convertir(String numero, bool mayusculas,string moneda="PESOS")
        {

            String literal = "";
            String parte_decimal;
            //si el numero utiliza (.) en lugar de (,) -> se reemplaza
            numero = numero.Replace(".", ",");

            //si el numero no tiene parte decimal, se le agrega ,00
            if (numero.IndexOf(",") == -1)
            {
                numero = numero + ",00";
            }
            //se valida formato de entrada -> 0,00 y 999 999 999,00
            r = new Regex(@"\d{1,9},\d{1,2}");
            MatchCollection mc = r.Matches(numero);
            if (mc.Count > 0)
            {
                //se divide el numero 0000000,00 -> entero y decimal
                String[] Num = numero.Split(',');

                string MN = " M.N.";
                if (moneda != "PESOS")
                    MN = "";

                //de da formato al numero decimal
                parte_decimal =moneda+" "+ Num[1] + "/100"+MN;
                //se convierte el numero a literal
                if (int.Parse(Num[0]) == 0)
                {//si el valor es cero
                    literal = "cero ";
                }
                else if (int.Parse(Num[0]) > 999999)
                {//si es millon
                    literal = getMillones(Num[0]);
                }
                else if (int.Parse(Num[0]) > 999)
                {//si es miles
                    literal = getMiles(Num[0]);
                }
                else if (int.Parse(Num[0]) > 99)
                {//si es centena
                    literal = getCentenas(Num[0]);
                }
                else if (int.Parse(Num[0]) > 9)
                {//si es decena
                    literal = getDecenas(Num[0]);
                }
                else
                {//sino unidades -> 9
                    literal = getUnidades(Num[0]);
                }
                //devuelve el resultado en mayusculas o minusculas
                if (mayusculas)
                {
                    return (literal + parte_decimal).ToUpper();
                }
                else
                {
                    return (literal + parte_decimal);
                }
            }
            else
            {//error, no se puede convertir
                return literal = null;
            }
        }

        /* funciones para convertir los numeros a literales */

        private String getUnidades(String numero)
        {   // 1 - 9
            //si tuviera algun 0 antes se lo quita -> 09 = 9 o 009=9
            String num = numero.Substring(numero.Length - 1);
            return UNIDADES[int.Parse(num)];
        }

        private String getDecenas(String num)
        {// 99
            int n = int.Parse(num);
            if (n < 10) {//para casos como -> 01 - 09
                return getUnidades(num);
            }
            else if (n > 19)
            {//para 20...99
                String u = getUnidades(num);
                if (u.Equals(""))
                { //para 20,30,40,50,60,70,80,90
                    return DECENAS[int.Parse(num.Substring(0, 1)) + 8];
                }
                else
                {
                    return DECENAS[int.Parse(num.Substring(0, 1)) + 8] + "y " + u;
                }
            }
            else
            {//numeros entre 11 y 19
                return DECENAS[n - 10];
            }
        }

        private String getCentenas(String num)
        {// 999 o 099
            if (int.Parse(num) > 99)
            {//es centena
                if (int.Parse(num) == 100)
                {//caso especial
                    return " cien ";
                }
                else
                {
                    return CENTENAS[int.Parse(num.Substring(0, 1))] + getDecenas(num.Substring(1));
                }
            }
            else
            {//por Ej. 099
                //se quita el 0 antes de convertir a decenas
                return getDecenas(int.Parse(num) + "");
            }
        }

        private String getMiles(String numero)
        {// 999 999
            //obtiene las centenas
            String c = numero.Substring(numero.Length - 3);
            //obtiene los miles
            String m = numero.Substring(0, numero.Length - 3);
            String n = "";
            //se comprueba que miles tenga valor entero
            if (int.Parse(m) > 0)
            {
                n = getCentenas(m);
                return n + "mil " + getCentenas(c);
            }
            else
            {
                return "" + getCentenas(c);
            }

        }

        private String getMillones(String numero)
        { //000 000 000
            //se obtiene los miles
            String miles = numero.Substring(numero.Length - 6);
            //se obtiene los millones
            String millon = numero.Substring(0, numero.Length - 6);
            String n = "";
            if (millon.Length > 1)
            {
                n = getCentenas(millon) + "millones ";
            }
            else
            {
                n = getUnidades(millon) + "millon ";
            }
            return n + getMiles(miles);
        }

    }

Y se utiliza así:


//creamos el objeto
Moneda oMoneda = new Moneda();

//primer parametro es la cantidad en string
//segundo parametro es si queremos que sea mayuscula
//tercer parametro la moneda
string resultado = oMoneda.Convertir("50.56", true,"PESOS"));

Llenar una cadena a una longitud especificada con un carácter repetido en C# .Net

Esta función sirve para llenar una cadena la cual necesitemos que tenga a la izquierda un carácter repetidamente hasta llegar a una longitud, por ejemplo si deseamos tener cadenas con una longitud de 5 caracteres y con el formato 00001, esta función podría funcionar para llenar un número que nos llegue como 1 y ponerle los 0 a la izquierda hasta que sea 5 su longitud.

La función recibe 3 parámetros, el primero es el carácter para rellenar, el segundo es la longitud deseada y el tercer es la cadena original. Nos regresara la cadena rellenada. La clase es la siguiente:


public class StringTool
{

        public static string FillCharacterStart(char character, int length,string word )
        {
            string result = word;
            for (int i=word.Length;i&lt;length;i++)
            {
                result = character + result;
            }

            return result;
        }
}

Y así se utiliza:


string cadena = StringTool.FillCharacterStart('0', 5, "1");

//resultado: 00001

¿Cómo poner el formato correcto de fecha para facturación electrónica 3.3 en C# .Net? cfdi SAT

Para ponerle el formato correcto para un xml que será timbrado con las reglas de cfdi 3.3, vamos a aplicar el formato a un DateTime de la siguiente forma:


//teniendo el dato en un DateTime en una variable llamada Fecha

string sFechaSAT= Fecha.ToString("yyyy-MM-ddTHH:mm:ss");

Así tu fecha tendrá el formato correcto para el timbrado.

¿Cómo obtener una cadena (string) a partir del contenido de un archivo en C# .Net?

En c# es muy fácil obtener una cadena del contenido de un archivo, esto es algo muy útil ya que podemos almacenar información en texto plano en archivos y esta misma obtenerla en cadenas para darle alguna funcionalidad. Por ejemplo un archivo que contenga el cuerpo de un e-mail en .html y así leerlo como string, manipularlo y después enviarlo.

Anexo una clase que tiene un método estático el cual regresa un string a partir de la ruta de un archivo:


 public class Archivo
    {

        public static string GetStringOfFile(string pathFile)
        {
            try
            {
                var contenido = File.ReadAllText(pathFile);

                return contenido;
            }
            catch (Exception ex)
            {
                return string.Empty;
            }
        }
    }

Y así se utiliza:


string ContenidoArchivo= Archivo.GetStringOfFile("Carpeta/otraCarpeta/AlgunArchivo.html");

Funciona con cualquier extensión: .html, .txt, .xml etc etc.