sábado, 26 de marzo de 2022

Curso avanzado de C#, Usando Ampliación y Reducción de Conversiones

Podemos clasificar las conversiones como de ampliación (Widening)  o estrechamiento (Narrowing). El código que realiza una conversión. También puede ser también implícito o explícito.

En una conversión de ampliación, el tipo de datos de destino puede contener cualquier valor proporcionado por los datos de origen tipo. En una conversión de reducción, el tipo de datos de destino no puede contener todos los valores posibles mantenidos por el tipo de datos de origen. Por ejemplo, una variable int puede contener valores enteros entre –2,147,483,648 y 2,147,483,647.
Una variable short puede contener valores enteros solo entre –32,768 y 32,767. Eso significa que convertir desde un short a un int es una conversión de ampliación porque un int puede contener cualquier valor short. Una conversión de ampliación siempre tiene éxito.

En contraste, la conversión de un int a un short es una conversión de estrechamiento porque un short no puede  mantener todos los valores posibles en un int. Eso no significa una conversión de short a int puede fallar, sin embargo. Si una variable int tiene un valor que puede caber en un short, como 100 o –13,000, la conversión tendrá éxito. Si el int tiene un valor que no cabe en un corto, como 70.000, la conversión falla.

De forma predeterminada, C # no lanzará una excepción si una conversión de reducción produce un error para enteros o tipos de coma flotante. Para los enteros, trunca el resultado y continúa como si nada hubiera salido mal, para los tipos de coma flotante, el programa establece el valor de la variable de resultado en Infinito si el valor no se ajusta y continúa ejecutándose.

Tenemos varias formas de hacer que C# lance una excepción para las conversiones enteras de estrechamiento no válidas.   

Podemos usar un bloque marcado como se muestra en el siguiente código. 

public void Chequeo()
{
   int grande = 1000000;
   short pequeño = (short)grande;
}

Dentro del bloque el programa lanzará una excepción OverflowException cuando la conversión del Int grande al short pequeño falla.

El bloque Chequeo no protege el código dentro de los métodos llamados dentro del bloque. Por ejemplo, si este código llamara al método CalculaImpuestos  y realizamos una conversión de estrechamiento. Si el método falla, el programa no lanzará una excepción.

También podemos hacer un programa para lanzar excepciones para conversiones de enteros no válidas, para ello, abrimos la página de propiedades del proyecto, seleccionamos la pestaña Compilación, y haciendo clic en el botón Avanzadas, muestra un cuadro de diálogo de Configuración avanzada de compilación.

Usando Ampliación y Reducción de Conversiones

Curso avanzado de C#, Usando Ampliación y Reducción de Conversiones

Nos aseguramos de que el check de Comprobar el desbordamiento y subdesbordamiento aritmético  está marcada. 

Comprobar el desbordamiento y subdesbordamiento aritmético


Si un programa guarda un valor de doble precisión en una variable flotante, El código debe verificar explícitamente el resultado para ver si está configurado en Infinito o para detectar un desbordamiento. El código también deberá comprobar para que capture las condiciones de desbordamiento.

Errores comunes: 
Realización de conversiones reducidas que resultan en desbordamientos

Los programadores principiantes a menudo no se dan cuenta de que el programa no se quejará si se realiza una conversión de reducción que resulta en un desbordamiento. Para evitar errores confusos, conviene obligar al programa a lanzar excepciones en esos casos.

El siguiente código utiliza el método EsIndeterminado de tipo flotante para determinar si el la conversión de estrechamiento causa un desbordamiento:

double grande = -1E40;
float pequeño = (float)grande;
if (float.EsIndeterminado(pequeño)) throw new OverflowException();

Realización de conversiones de coma flotante 

El programa continuará ejecutándose si una conversión o cálculo de coma flotante produce un desbordamiento. Para evitar errores, debemos comprobar el resultado de  las variables Infinity y NegativeInfinity.

Utilizando conversiones implícitas y explícitas

Una conversión implícita es aquella en la que el programa convierte automáticamente un valor a otro sin ninguna declaración adicional para indicarle que realice la conversión. En contraste, una la conversión explícita utiliza un operador o método adicional, como un operador de conversión o un método de análisis para indicar explícitamente al programa de cómo realizar la conversión.
Debido a que las conversiones de reducción pueden resultar en una pérdida de datos, un programa de C# no realizará una conversión de reducción automática, por lo que no se habilitará una conversión de reducción implícita.
Para ello debe obligarse al código d a utilizar explícitamente algún tipo de operador o método de conversión para dejar en claro que tenemos la intención de realizar La conversión, con un posible resultando de pérdida de datos. 

En contraste, una conversión de ampliación siempre tendrá éxito, por lo que un programa de C# puede hacer conversiones implícitas de ampliación implícitamente sin utilizar un operador o método de conversión explícito. Podemos utilizar un operador de conversión, pero no estamos obligados a hacerlo. El siguiente código muestra ejemplos de conversiones implícitas y explícitas:

// Conversión de estrechamiento. Debe ser explícita.
double value1 = 10; float value2 = (float)value1;

//  Conversión de ensanchamiento, puede ser implicita.
int value3 = 10; long value4 = value3;

Para los tipos por referencia, la conversión a una clase o interfaz ancestral directa o indirecta es una conversión de ampliación, Así que un programa puede hacer la conversión implícitamente.  La siguiente entrada incluye más información sobre conversión de tipos por referencia.


sábado, 19 de marzo de 2022

Curso avanzado de C#. Propiedades Indexadas, Tipos genéricos y Métodos genéricos

Las propiedades indexadas, o indexadores, se comportan de manera un poco diferente a las propiedades estándar. El propósito principal de las propiedades indexadas es permitir el "acceso a grupos de elementos de forma similar a como se accede a un array". En otras palabras, si tenemos clases que utilizan matrices u otros tipos de colecciones, debemos considerar el uso de indizadores para acceder a los valores de estas colecciones internas. Las propiedades estándard discutidas hasta ahora se utilizan para acceder a valores individuales en clases, mientras que una propiedad indexada Se utilizará para encapsular un conjunto de valores.

Para mostrar el funcionamiento de las propiedades indexadas, necesitamos un ejemplo simple que lo ilustre. Una aplicación que trate con direcciones de red en un escenario de subredes IP. 

Curso avanzado de C#. Propiedades Indexadas, Tipos genéricos y Métodos genéricos


sábado, 12 de marzo de 2022

Curso avanzado de C#. Encapsulación

En OOP, el concepto encapsulación se utiliza para describir el concepto de empaquetar las propiedades, métodos, y cualquier otro miembro de la clase. La encapsulación también se utiliza para referirse a la "ocultación" de los detalles de implementación de la funcionalidad de la clase y también la protección de las características de la clase.

Curso avanzado de C#. Encapsulación


sábado, 5 de marzo de 2022

Curso avanzado de C#. Sobrecarga de Métodos

Un método es definido por su modificador, el tipo de retorno, el nombre, el número y el tipo de los argumentos. Un método también tiene una firma. La firma de un método, es lo que identifica de forma única el método de cualquier otro método que tenga el mismo nombre. Cuando llamamos a un método en nuestro código, el compilador busca un método con la firma correcta. La firma consiste en el nombre del método, el tipo de datos y la cantidad de parámetros en el método. El tipo o parámetro en un método, además  puede ser un tipo pasado por valor, un tipo pasado por referencia o un parámetro de salida. El tipo de retorno, por tanto no es el componente único de una firma de método.

Sobrecarga de Métodos


sábado, 26 de febrero de 2022

Curso avanzado de C#.Constructores y métodos

A parte de la asignación de valores después de instanciar una clase, hay otra forma, de  asignar dichos valores a los miembros de un objeto. Esto se hace a través de un constructor.  Un constructor es un método que se llama siempre que se crea una instancia de un objeto. Podemos utilizar constructores en nuestros archivos de clase para establecer valores iniciales para algunos o todos los datos miembros en los objetos que creamos. Si no definimos un constructor C# crea su propio constructor. El constructor por defecto establece los valores de Cada variable miembro a su valor por defecto. 

Los constructores tienen una sintaxis específica, como se muestra aquí: 

  // sintaxis para un constructor
 public NombreClase()
 {
       //Instrucciones de inicialización opcionales;
 }
 // Definición de la clase estudiante
 class Estudiante
 {
   public static int CuentaEstudiante;
    public string Nombre;
    public string Apellido;
    public string Grado;
        // Constructor 1 de la clase Estudiante
        public Estudiante(string nombre, string apellido, string grado)
        {
            this.Nombre = nombre; this.Apellido = apellido; this.Grado = grado;
        }
        // Constructor n de la clase Estudiante
        public Estudiante()
        {
        }
}

En el ejemplo anterior hay dos constructores. Los constructores son públicos porque deben ser accesibles fuera de la clase.  Esto es necesario para permitir que el objeto se inicialice cuando se crea. El constructor toma el mismo nombre que la clase. Dentro de las llaves adjuntas, las declaraciones de inicialización son opcionales. Un constructor es un método pero no incluye ningún tipo de retorno, ni siquiera nulo. Incluir un tipo de retorno en un constructor es una sintaxis incorrecta y generará una advertencia del compilador.

El primer constructor es el no predeterminado y acepta tres valores de cadena y los utiliza para inicializar las variables miembro. El segundo constructor es predeterminado y no incluye declaraciones ni toma argumentos.

Este es el tipo de constructor que el compilador se genera si el desarrollador no crea otros constructores. Este constructor inicializa las variables miembro con sus valores por defecto. Cuando creamos un nuevo objeto, el compilador tiene la opción de usar cualquiera de los constructores disponibles declarados en la clase, o ninguno. En el ejemplo anterior podemos llamar al constructor no predeterminado, pasando los valores del nombre, apellido y grado. Si no proporcionamos ningún valor, se llamará al constructor por defecto. Además, no podemos llamar al  constructor no predeterminado anterior
si sólo rellenamos algunos valores. Es todo o nada. Los constructores predeterminados se utilizan solo cuando no se llama a ningún otro constructor.  

Definiendo métodos

Los métodos son los componentes de una aplicación que desglosar los requisitos informáticos de nuestra aplicación en piezas más pequeñas de funcionalidad.  Una buena práctica de programación dicta que los métodos deben realizar funciones discretas en nuestro código y que el método realiza solo lo que es  necesario para lograr el resultado deseado. Algunos argumentan que codificar de esta manera genera un código que ocupa más recursos  debido a que el sistema operativo debe mantener los indicadores de instrucciones y las referencias para todas las llamadas al método función,  pero hace que nuestro código sea más fácil de leer y mantener. Si nuestro programa está generando errores, es mucho más fácil localizarlos. 


Un método es un trozo de código definido por un nombre, una firma, un bloque de declaración  y una variable de retorno opcional. La sintaxis de un método siguiente: 

// sintaxis de un método

modificador return type nombre_metodo(optional argumentos)
{
   intrucciones;
}

En el ejemplo anterior, el modificador puede ser público, privado, etc. El tipo de retorno puede ser cualquier tipo de C# válido (por valor o referencia) pero también puede ser la palabra clave void, que indica que el método no devuelve ningún valor. El nombre se usa para identificar el método en el código y se utiliza para llamar al método. Los los paréntesis incluyen argumentos opcionales para el método. Un método puede tener ninguno o varios argumentos.

La llaves encierran la funcionalidad del método en forma de instrucciones. Estas instrucciones pueden ser cualquiera de C# y también puede incluir una declaración de devolución opcional return sólo si el método tiene declarado un tipo de retorno. 

Es ilegal incluir una declaración de devolución en un método declarado como void. A continuación crearemos dos métodos para la clase de Estudiante. Un método recuperará el nombre y apellido del estudiante, los concatena y devuelve el nombre. El método de llamada no devuelve valor, pero imprimirá el nombre en la ventana de la consola. 

class Estudiante
 {
   public static int CuentaEstudiante;
    public string Nombre;
    public string Apellido;
    public string grado;
   // sintaxis de un método
    public string concatenaNombre()
    {
      string NombreCompleto = this.Nombre + " " + this.Apellido;
        return NombreCompleto;
    }
    // sintaxis de un método
    public void MuestraNombre()
    {
      string nombre = concatenaNombre();
        Console.WriteLine(nombre);
        Console.ReadLine();
    }
}
class Programa
{
   static void Main(string[] args)
    {
      Estudiante primerEstudiante = new Estudiante();
        Estudiante.CuentaEstudiante++; Estudiante segundoEstudiante = new Estudiante();
        Estudiante.CuentaEstudiante++; primerEstudiante.Nombre = "Juan";
        primerEstudiante.Apellido = "González"; primerEstudiante.grado = "sexto";
        segundoEstudiante.Nombre = "Antonio"; segundoEstudiante.Apellido = "Fernández";
        segundoEstudiante.grado = "dos"; primerEstudiante.MuestraNombre();
    }
}

Análisis

Este ejemplo muestra el uso de métodos tanto dentro de la clase como en el método principal de la aplicación. Hemos añadido dos métodos. El primer método se llama concatenaNombre() y devuelve un valor de cadena. Lo hemos declarado público, y con un valor de retorno de cadena. El método no tiene parámetros simplemente declara una variable llamada NombreCompleto de tipo cadena. Luego usa la funcionalidad de concatenación de cadenas de C# y combina la variable Nombre con un espacio y la variable Apellido para crear el nombre completo para el alumno.

Asigna esto a la variable NombreCompleto y luego la envía a la función de llamada con la declaración de retorno Return. La función que llama a concatenaNombre()  es otro método simple que hemos creado, llamado MuestraNombre() utiliza el tipo de retorno void, lo que significa que no devuelve un valor y no tiene una declaración de retorno en el bloque de instrucción. Declara una variable de tipo cadena llamada nombre y utiliza el valor de retorno del método concatenaNombre() para Asignar a la variable nombre. Luego escribe el valor en la ventana de la consola.

En el método principal de la aplicación, agregamos una nueva declaración al final del método, primerEstudiante.MuestraNombre(); Esta declaración utiliza el objeto primer estudiante Objeto creado en el código y llama a su método público MuestraNombre(). La ejecución cambia a este método en el código del objeto. El método crea una variable, y luego en la instrucción de asignación, llama al método concatenaNombre() del mismo objeto. 

La ejecución ahora pasa a este método donde La variable NombreCompleto se crea y se utiliza en una instrucción que concatena el nombre y el apellido.
Debido a que el nombre de cadena de declaración concatenaNombre(); ha sido el responsable de llamar a este método, el compilador ha mantenido un seguimiento en la pila de memoria y sabe dónde está el valor de retorno que necesita la declaración de retorno de concatenaNombre(), cuando finaliza ese método, devuelve el valor al método que le llamó y asigna a la variable la concatenación generada. Ahora el método MuestraNombre () ya puede mostrar el nombre completo en la ventana de la consola. 


Los métodos  pueden aceptar varios valores entrantes. Esto es posible mediante el uso de parámetros y argumentos. La firma del método que acepta valores es de este tipo: 

// sintaxis de un método que toma valores

 modificador return tipo nombre(parametros)
 {
   instrucciones;
}

Desafortunadamente, el uso de los términos parámetros y argumentos suele ser mal utilizado. Cuando se trata de métodos, se utiliza el término parámetro para identificar los marcadores de posición en la firma del método, mientras que los argumentos son los valores reales que se pasan al método. 

// ejemplo de firma de métod que acepta valores
 public int suma(int num1, int num2)
 {
   return num1 + num2;
 }
 int ValorSuma = suma(2, 3);


En el ejemplo anterior, el método suma es público y devuelve un valor entero. Dentro de los paréntesis vemos int num1, int num2. Estos son los parámetros del método. Debemos indicar el tipo de datos que se espera de estos parámetros. Esto ayuda al compilador a capturar asignaciones inválidas cuando se llama al método es  llamado; num1 y num2 son los nombres de los parámetros. La última línea en el código de ejemplo llama al método de suma y pasa dos valores. Estos dos valores son los argumentos de la llamada al método. Podemos ver ahora de dónde viene la confusión  y por qué estos dos términos se usan indistintamente a veces.

Hay que pensar en los parámetros como en marcadores de posición en la firma del método, y los argumentos son los valores que se pasan en a estos marcadores de posición. Los métodos pueden recibir objetos como argumento. Otra distinción importante, es cuando se pasan argumentos por valor, se pasa una copia del valor al método, mientras que al pasar argumentos por referencia, se pasa una referencia (una dirección de memoria) y no se pasa todo el objeto.

Podríamos ocupar bastante memoria si pasamos un objeto entero a un método. Cuando pasamos argumentos por valor, el método actúa sobre una copia local de la variable y no cambia el valor original. Al pasar un argumento por referencia si actuamos sobre el argumento, modificamos su valor. Esto se ve en el siguiente ejemplo. 

Pasando valores a métodos [value_type_passing] 

class Estudiante
 {
        public string Nombre;
        public string Apellido;
        public string grado;
 }
    class Programa
    {
        static void Main(string[] args)
        {
            int num1 = 2; int num2 = 3;
            int resultado;
            Estudiante primerEstudiante = new Estudiante();
            primerEstudiante.Nombre = "Juan";
            primerEstudiante.Apellido = "González";
            primerEstudiante.grado = "seis";
            resultado = suma(num1, num2);
            Console.Write("La suma es: ");
            Console.WriteLine(resultado);
            // devuelve 5
            Console.WriteLine();
            cambiaValores(num1, num2);
            Console.WriteLine();
            Console.WriteLine("Regreso a cambiaValores()");
            Console.WriteLine(num1);
            // devuelve 2
            Console.WriteLine(num2);
            // devuelve 3
            Console.WriteLine();
            Console.WriteLine("El nombre de primerEstudiante es " + primerEstudiante.Nombre);
            cambiaNombre(primerEstudiante);
            Console.WriteLine();
            Console.WriteLine(" El nombre de primerEstudiante es " + primerEstudiante.Nombre);
            Console.ReadLine();
        }

        static int suma(int valor1, int valor2)
        {
            Console.WriteLine("En el método suma()");
            return valor1 + valor2;
        }

        static void cambiaValores(int valor1, int valor2)
        {
            Console.WriteLine("En el método cambiaValores()");
            Console.WriteLine("valor1 es " + valor1);
            // devuelve 2 Console.WriteLine("valor2 is " + valor2);
            // devuelve 3 Console.WriteLine();
            Console.WriteLine("Cambiando valores");
            valor1--; valor2 += 5;
            Console.WriteLine();
            Console.WriteLine("valor1 es ahora" + valor1);
            // devuelve 1 Console.WriteLine("valor2 is now " + valor2);
            // devuelve 8
        }
        static void cambiaNombre(Estudiante refValue)
        {
            Console.WriteLine();
            Console.WriteLine("En cambiaNombre()");
            refValue.Nombre = "Jorge";
        }
}

Análisis

La clase Estudiante está simplificada para este ejemplo, solo tenemos tres campos miembro en el método principal del programa, declaramos cuatro variables, tres de tipo int y una de tipo estudiante. Asignamos valores a num1, num2 y a los miembros del objeto Estudiante llamado primerEstudiante. La variable de resultado se asignará más adelante. El primer método al que llamamos es el método que suma, pasamos a num1 y num2 como argumentos al método.

Dentro de suma(), escribimos un mensaje para indicar que estamos dentro de este método. A continuación, agregamos los dos valores y devolvemos el resultado al método que lo ha llamado. Después mostramos ese resultado en la ventana para ver como el método suma() ha sumado ambos valores, luego mostramos un ejemplo de cómo el método utiliza copias de los valores si estos son suministrados por valor. Llamamos al método cambiaValores() donde le pasamos de nuevo num1 y num2. En cuya salida mostramos los valores numéricos de los dos parámetros valor1 y valor2.

Con esto demostramos que pasamos los mismos valores para num1 y num2. Luego  indicamos que cambiaremos estos valores y decrementamos en 1 el valor1 e incrementaremos en 5 el valor2. Finalmente el método cambiaValores(), genera los nuevos valores para valor1 y valor2. 

De vuelta a la función main, mostramos los valores de num1 y num2 de nuevo para mostrar que estas variables no han sido cambiadas por el método cambiaValores() solo se han cambiado las copias locales, no los valores originales. 

Para ver cómo se ven afectados los tipos por referencia en las llamadas a métodos, ahora mostramos el primer nombre del El objeto primerEstudiante que creamos para mostrar que su valor es Juan, el valor que se le asignó al principio el código. Luego llamamos a otro método llamado cambiaNombre(), que toma una variable por referencia de tipo Estudiante, y pasa el primer estudiante como tipo por referencia al método. Dentro de este método, cambiamos el nombre del primer estudiante a Jorge. Después de volver del método, escribimos el primer nombre del primer estudiante y vemos que de hecho lo hemos cambiado. Esto muestra claramente que al pasar una variable como referencia a un método, obtenemos como resultado el cambio del valor original, muy diferente del tipo pasado por valor.  La Figura muestra la salida del código anterior.