Mostrando las entradas para la consulta creación de tipos por referencia ordenadas por fecha. Ordenar por relevancia Mostrar todas las entradas
Mostrando las entradas para la consulta creación de tipos por referencia ordenadas por fecha. Ordenar por relevancia Mostrar todas las entradas

sábado, 9 de julio de 2022

Curso avanzado de C#. Creación e implementación de jerarquías de clases

Heredar de una clase base La sección "Creación de tipos de referencia", explica cómo crear clases. El siguiente código muestra la definición de una clase Persona simple y debe ser familiar para  nosotros

Public Class Persona

{

       Public String Nombre { Get; Set; }

       Public String Apellido { Get; Set; }

       Public Persona(String nombre, String apellido)

       {

             // Valida el nombre y el apellido

             If (( nombre == null) || (nombre .Length < 1))

                    Throw New ArgumentOutOfRangeException(

                    "Nombre", nombre,

                    "El nombre no puede ser nulo o quedar en blanco.");

             If ((apellido == null) || (apellido .Length < 1))

                    Throw New ArgumentOutOfRangeException(

                    "Apellido", apellido,

                    "El  apellido no puede ser nulo o quedar en blanco.");

                    // Guarda el nombre y el apellido.

                    Nombre = nombre;

                    Apellido = apellido;

       }

}

La clase Persona contiene dos propiedades de cadena implementadas automáticamente: Nombre y Apellido. (Una clase utilizada por una aplicación real probablemente tendría muchas más propiedades para contener información como como la dirección postal, números de teléfono y direcciones de correo electrónico). Nuestro constructor toma los nombres y apellidos como parámetros, realiza alguna validación y guarda los valores en las propiedades Nombre y Apellido.

Curso avanzado de C#. Creación e implementación de jerarquías de clases
Fotos similares a esta aquí


Ahora supongamos que queremos  crear una clase de Empleado que tenga las propiedades Nombre, Apellido y NombreDepartamento. Podemos construir esta clase desde cero, pero necesitaremos las mismas propiedades que las de la clase Persona y necesitaremos las mismas validaciones, por lo que crearlo desde cero requeriría duplicar  todo ese código. Una mejor solución es derivar la clase Empleado de la clase Persona para que herede de la clase padre los campos, propiedades, métodos y eventos. Un empleado es una especie de persona, por lo que tiene sentido que la clase Empleado derive desde la clase Persona. No hay ninguna razón por la que el mismo código dentro de la clase Persona no funcionara también para los objetos  de la clase Empleado. 

Terminología de clases

Hay mucha terminología en torno a las jerarquías de clases. Cuando derivamos una clase de otra, la nueva clase hereda todo el código incluido en la clase original. En este caso, la clase original se llama padre clase, clase base o superclase. La nueva clase se llama clase derivada, hija, secundaria, o subclase. Derivar una clase de otra se llama subclasificación. 

_________________________________________________


Para derivar una clase de otra, simplemente hay que poner el nombre de la clase con dos puntos y luego el nombre de la clase principal. El siguiente código muestra una clase  empleado que se deriva de la clase persona. 

Public Class Empleado :  Persona

{

    Public String NombreDepartamento { Get; Set; }

}

En este ejemplo, la clase Empleado hereda los campos, propiedades, métodos y eventos definidos por la clase Persona. También agrega una nueva propiedad, Nombre Departamento. Aunque una clase secundaria hereda la mayoría del código en su clase padre, no hereda el constructor de la clase. En este punto, un programa podría utilizar el siguiente código para crear un objeto Empleado sin inicializar sus propiedades Nombre y Apellido:

Empleado empleado = new Empleado();

Dado que este código no inicializa las propiedades Nombre y Apellido del empleado, estas tienen los valores nulos, lo que anula el propósito del constructor de la clase Persona. La solución es hacer que los constructores de la clase secundaria llamen a los constructores de la clase principal. 

Llamar a constructores de clases principales 

Para asegurarse de que se llama al constructor de la clase Persona para validar el Nombre y Apellido del empleado. Debemos darle un constructor a la clase Empleado, con  una lista de argumentos del constructor seguida por dos puntos, la clase base y cualquier parámetro que deseemos pasar al constructor de la clase base.

En este ejemplo, el constructor Empleado debe pasar el nombre y apellido que recibe al Constructor de la clase de persona. El código resaltado muestra dónde el constructor llama al constructor de la clase base : 

Public Class Empleado :  Persona

{

      Public String NombreDepartamento { Get; Set; }

      Public Empleado(String Nombre, string Apellido,

      string nombredepartamento)

            :base(Nombre, Apellido)

      {

            // Valida NombreDepartamento.

            If ((nombredepartamento == null) || (nombredepartamento.Length < 1))

            Throw New ArgumentOutOfRangeException(

            "nombredepartmento", nombredepartmento,

            "NombreDepartamento no debe ser nulo o estar en vacío.");

            // Graba el Nombre del Departamento.

            NombreDepartmento = nombreDepartmento;

      }

}

Si la clase base tiene varios constructores, la clase secundaria puede usar la palabra clave base para invocar cualquiera de ellos. El programa usa los argumentos que siguen a la palabra clave base para averiguar qué constructor utilizar. 

Nota: Cuando un constructor utiliza la palabra clave base para invocar un constructor de la clase base, El constructor de la clase base se ejecuta antes que el cuerpo del hijo. 

Si tanto la clase principal como la secundaria tienen constructores, el constructor de la clase secundaria debe invocar uno de los constructores de la clase padre. Esto significa que la llamada al constructor en la clase base que hemos resaltado arriba, requiere un fragmento de código. Si eliminamos ese código, Visual Studio muestra el mensaje de error "JerarquiaPersona no contiene un constructor que tome 0 argumentos". (Aquí, JerarquiaPersona es el espacio de nombres que contiene la clase Persona.) El constructor de la clase Empleado está tratando implícitamente de acceder a un constructor de Persona que no toma parámetros y no puede encontrar uno. Una rareza de este sistema es que podemos crear una clase Empleado sin constructores aunque  permite al programa crear una instancia de la clase Empleado sin invocar un constructor de la clase Persona. Eso significa que la siguiente definición para la clase Empleado es legal: 

Public Class Empleado :  Persona

{

      Public String NombreDepartamento { Get; Set; }

}

Si deseamos evitar que el programa eluda los constructores de la clase principal, debemos dotar la clase secundaria de al menos un constructor.

Llamar a constructores de la misma clase 

A menudo, es conveniente dar a una clase varios constructores para realizar diferentes tipos de inicialización. Dependiendo de qué parámetros se pasen al constructor. En ese caso, varios constructores puede que necesiten realizar las mismas tareas. Por ejemplo, supongamos que la clase Persona tiene las propiedades Nombre y Apellido y queremos permitir que el programa cree un objeto Persona especificando solo el nombre o nombre y apellido. 

Class Persona

{

     Public String Nombre { Get; Set; }

     Public String Apellido { Get; Set; }

     // Constructor con nombre.

     Public Persona(String nombre)

     {

          Nombre = nombre;

     }

     // Constructor con Nombre y Apellido.

     Public Persona(String nombre, String apellido)

     {

          Nombre =  nombre;

          Apellido = apellido;

     }

}

El primer constructor toma un nombre como parámetro y lo almacena en la propiedad Nombre. El segundo constructor toma el nombre y el apellido como parámetros y guarda sus valores en las Propiedades Nombre y Apellido. En este código, el segundo constructor comienza realizando el mismo trabajo que el primer constructor hace cuando guarda el nombre. En un escenario mas complicado en el que los constructores realizan tareas más difíciles, esta repetición de código sería un problema. Significaría varias copias del mismo código para implementar, depurar y mantener. Una forma de evitar esta duplicación de código es hacer que un constructor llame a otro. En este podríamos reescribir el segundo constructor para que llame al primer constructor para que pueda manejar el parámetro de nombre. Para ello, hacemos que el constructor invoque a un segundo constructor de la misma manera que se invoca a un constructor de la  clase base, excepto que utiliza la palabra clave this en lugar de la palabra clave base. 

El siguiente código muestra cómo el segundo constructor de la clase Persona puede invocar a su primer constructor. Se resalta el código que invoca al primer constructor. 

// Constructor con Nombre y Apellido.

Public Persona(String nombre, String apellido)

      :this(nombre)

{

      Apellido = apellido;

}

Nota: Cuando un constructor utiliza la palabra clave this para invocar un segundo constructor en la misma clase, el segundo constructor se ejecuta antes de que se ejecute el cuerpo del primer constructor. 

Si derivamos la clase Empleado de la clase Persona y la clase Empleado incluye una propiedad NombreDepartamento. Es posible que queramos diferentes constructores de empleados que puedan tomar como parámetros nombre, nombre y apellido, o nombre y apellido y nombre de departamento. Estos constructores pueden utilizar la misma técnica que se muestra en esta versión de la clase Persona para hacer que los constructores más complicados invoquen a los más simples. Los constructores Empleado también pueden utilizar la palabra clave base descrita en la sección anterior para invocar a los constructores de la clase Persona. Por ejemplo, el constructor Empleado(nombre, apellido,nombredepartamento) podemos utilizar esta técnica para invocar al constructor Empleado( nombre, apellido) y ese constructor puede utilizar la palabra clave  base para invocar al constructor de Persona(nombre, apellido). 







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, 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, 19 de febrero de 2022

Curso avanzado de C#.Tipos por referencia y modificadores

La Programación Orientada a Objetos OOP permite al desarrollador modelar objetos del mundo real en el código mediante el uso de clases. Si creamos una aplicación para un  cajero automático. Necesitará tratar con objetos tales como clientes, cuentas, depósitos, retiros, saldos, etc. Es mucho más fácil escribir código para modelar estos objetos creando una representación de dichos objetos en el código y luego asignar a estos objetos de código las mismas características y funcionalidad que la que tienen los objetos del mundo real. Una clase es un archivo que contiene el código necesario para modelar los objetos del mundo real. Una clase es como una plantilla de objetos. 

Curso avanzado de C#.Tipos por referencia y modificadores


La razón para hacer referencia a estos tipos de objeto como tipos por referencia es que la variable declarada para el tipo mantiene sólo una referencia al objeto real. 

En el código .NET, se utilizan dos secciones lógicas  distintas: de memoria y del ordenador. Se les llama pila (stack) y montón (heap). La pila es un área de memoria reservada por el sistema operativo para la ejecución. En la pila es donde .NET almacena tipos de datos simples en una cantidad relativamente pequeña de memoria utilizada para la ejecución del código. La mayoría de los tipos de datos simples serán creados y destruidos bastante rápido a medida que se ejecuta la aplicación, y por lo tanto la pila se  mantendrá limpia durante la ejecución del código. También es la razón por la que recibiremos excepciones de memoria insuficiente si ponemos un bucle infinito que se ejecuta y que almacena valores en la pila. 

El montón es un área de memoria mucho más grande que .NET Framework utiliza para almacenar los objetos que crea nuestro código basado en clases. Un objeto creado a partir de una clase puede requerir grandes cantidades de  memoria en función del tamaño de la clase. Las clases contienen tipos de datos simples para mantener los valores pertenecientes a las características de los objetos que está modelando. También contienen métodos que proporcionan la funcionalidad que muestra el objeto. 

.NET Framework utiliza la referencia hacia un objeto, que es su dirección de memoria. De este modo, si el código requiere copiar o asignar el objeto a otra variable, la memoria se conserva porque el compilador copia solo la dirección de memoria donde se encuentra almacenado y no el objeto en sí.  Las clases se crean con una sintaxis específica como se muestra aquí: 

class Miclase
 { 
// campos
 // propiedades 
// métodos 
// eventos 
// delegados 
// clases anidadas
 } 


El ejemplo anterior no dicta el orden de los componentes de la clase, sino que simplemente enumera los elementos que puede contener una clase. Además, los artículos enumerados no son obligatorios.  Si estuviéramos modelando un coche, los campos podrían consistir del modelo, marca, color, año, número de puertas, etc. Los campos también se conocen comúnmente como Miembros, miembros de datos y campos de datos.
Las propiedades están directamente relacionadas con los campos. Las propiedades se utilizan para permitir el acceso controlado a los campos. en nuestra clase. 


Los métodos se utilizan para proporcionar funcionalidad a los objetos. Los objetos del mundo real tienen funcionalidad.  Siguiendo la analogía del coche, un coche encender o apagar el motor y puede acelerar, o frenar, detenerse, etc. Estos son ejemplos de métodos.

Los eventos también son funcionalidades en el código, pero de una manera diferente. Son cosas que suceden como resultado de alguna influencia exterior. Por ejemplo, si un sensor en un automóvil detecta un problema, se eleva un evento y el ordenador del  automóvil "escucha" el evento que se está activando con lo que genera una advertencia. Los eventos son un mecanismo por el cual los objetos notifican a otros objetos cuando sucede algo. El objeto que provocó el evento es el editor de eventos (event publisher) y el objeto que recibe el evento es el suscriptor del evento (event subscriber ).

Un delegado es "un tipo que hace referencia a un método". Si pensamos en un delegado en términos de un escenario político. Por ejemplo, un político delegado es alguien que ha sido elegido para representar a una o más personas. En C#, un delegado se puede asociar con cualquier método que tenga una firma similar (tipos de argumento). 

Modificadores 

Los modificadores se utilizan en la declaración de tipos y los datos miembro de nuestros tipos por referencia. En la siguiente tabla enumeraremos los modificadores disponibles en C# junto con una descripción de lo que hacen. 


Modificador
Descripción
public
Modificador de acceso que declara la accesibilidad del tipo al que está asignado. Este es el nivel más permisivo. Se permite el acceso fuera del cuerpo de la clase o estructura. Los tipos por referencia y valor pueden ser declarados públicos. Los métodos también pueden ser declarados públicos.
private
Declara la accesibilidad del tipo que se le asigna. Es el menos permisivo, permite el acceso solo dentro del cuerpo de la clase o estructura Los tipos de referencia y valor pueden ser declarados privados. Los métodos también pueden ser declarados privados.
internal
Modificador de acceso que declara la accesibilidad al tipo al que está asignado.  Permite el acceso solo dentro de archivos en el mismo ensamblaje .NET.
protected
Modificador de acceso para miembros. Los miembros declarados como  protected son accesibles solamente desde dentro de la clase y en las clases derivadas.
abstract
Utilizado en clases,  indica que la clase no puede ser instanciada pero que sirve como clase base para otras clases en una jerarquía de herencia.
async
Configura el método o la expresión lambda a la que se aplica como método asíncrono.

Permite a los métodos llamar a procesos de larga duración sin bloquear el código de llamada.
const
Aplicado a un campo, indica que el campo no puede modificarse. Las constantes deben inicializarse en el momento de su creación.
event
Se utiliza para declarar eventos en el código.
extern
Se utiliza para indicar que el método se ha declarado e implementado externamente.  Se  puede usar esto con archivos DLL importados o conjuntos externos.
new
Cuando se utiliza con los miembros de una clase, este modificador oculta los miembros heredados de los miembros de la clase base. Es necesario hacer esto si hemos heredado un miembro de una clase base pero nuestra clase derivada necesita utilizar nuestra propia versión de ese miembro.
override
Se utiliza cuando heredamos la funcionalidad de una clase base que queremos cambiar. 
partial
Los archivos de clase pueden existir en varios archivos en el mismo ensamblaje. Este modificador dice el compilador que la clase existe en otro archivo o archivos en el ensamblaje.
readonly
Los miembros de solo lectura pueden asignarse solo durante la  declaración o en un constructor de clase. No se permite ningún otro medio para cambiar o asignar un valor a ese miembro.
sealed
Se aplica a clases. Las clases selladas no se pueden heredar.
static
Cuando se aplica a un miembro de la clase, significa que pertenece a la clase  solo y no a objetos específicos creados a partir de la clase. .NET Framework tiene muchos ejemplos de esto, como la clase Math o la clase String.
unsafe
C# es código administrado, lo que significa que las operaciones de memoria se utilizan de forma protegida. El uso de la palabra unsafe  declara un contexto que no es seguro en términos de gestión de memoria, los punteros de C++ son ejemplos de operaciones de memoria no segura. Las operaciones para utilizar punteros en C#, se deben declarar en un contexto no seguro
virtual
Si creamos una clase y queremos permitir que el método se sobrescriba en una clase derivada, podemos utilizar la palabra  virtual.
volatil
Cuando se aplica a un campo, el campo puede ser modificado por componentes  aparte de nuestro código, como por ejemplo el sistema operativo


Definiendo campos

Utilizamos campos para almacenar los datos que describen las características de las clases. Los campos se declaran como variables dentro de la clase y pueden ser de cualquier tipo y pasarse por valor o por referencia.

Los campos vienen en dos tipos básicos, como instancia y estáticos, y una clase puede contener un tipo o los dos. Un campo de instancia es el que utilizaremos más a menudo en nuestras clases. Los campos de instancia son aquellos contenidos dentro de cada objeto que se crea desde la definición de la clase. Cada campo de instancia contiene datos específicos para el objeto al que está asignado. Como ejemplo, crearemos una clase simple y luego crearemos dos instancias de la clase, estableciendo diferentes valores para los campos de la clase. 

Clase que muestra campos de instancia  [clase_estudiante] 

// crea una clase llamada estudiante
class Estudiante
    {
        public static int CuentaEstudiantes;
        public string Nombre;
        public string Apellido;
        public string Grado;
    }
    class Programa
    {
        static void Main(string[] args)
        {
            Estudiante primerEstudiante = new Estudiante();
            Estudiante.CuentaEstudiantes++;
            Estudiante segundoEstudiante = new Estudiante();
            Estudiante.CuentaEstudiantes++;
            primerEstudiante.Nombre = "Mariano";
            primerEstudiante.Apellido = "González";
            primerEstudiante.Grado = "primero";
            segundoEstudiante.Nombre = "Timoteo";
            segundoEstudiante.Apellido = "Pérez";
            segundoEstudiante.Grado = "segundo";
  Console.WriteLine(primerEstudiante.Nombre);        Console.WriteLine(segundoEstudiante.Nombre); Console.WriteLine(Estudiante.CuentaEstudiantes);
            Console.ReadLine();
        }
    }

Análisis

La primera parte del código crea una clase simple llamada Estudiante. En esta clase, creamos cuatro variables.

Una se declara como una variable estática de tipo int y se llama Cuentaestudiantes. Utilizamos esta variable para hacer un seguimiento de cuántos estudiantes hemos creado. Debido a que es estática, es una variable que se asigna a la clase, no a una instancia.  Cada una de las variables restantes son variables de instancia y se asignarán valores en cada objeto (instancia) de esta clase que hemos creado. 

Dentro del método principal, hemos creado dos instancias de la clase Estudiante: una llamada primerEstudiante y otra llamada segundo estudiante. 

Podemos hacerlo indicando primero el tipo de variable que deseamos utilizar. De la misma manera que creamos los tipos por valor, utilizaremos el nombre del tipo seguido del nombre de la variable.
En este caso, el nombre de la variable es en realidad el nombre de un objeto del tipo de la clase que creamos en el código. La palabra clave new dice al compilador que deseamos crear una nueva instancia del tipo de clase Estudiante. La nueva palabra clave es una instrucción para que el compilador vea la clase Estudiante, identificamos a los miembros y sus tipos de datos, luego reservamos suficiente memoria para almacenar el objeto y todos nuestros requisitos de datos. Después de creamos cada objeto, utilizamos la variable estática en la clase Estudiante y le agregamos uno. Esta La variable solo está disponible en la clase y no en los objetos de instancia, por lo que debemos utilizar el nombre de la Clase Estudiante para acceder a esta variable. Una vez que hemos creado las instancias,  les asignamos valores a los miembros. Utilizaremos el nombre de cada instancia para asignar valores a sus miembros. Y aquí es donde está la diferencia entre las variables estáticas y de instancia. Después de las asignaciones, se envían los valores a la consola. En este caso, solo se imprimen los nombres de cada instancia Estudiante para mostrar que los valores son realmente únicos para cada instancia. Podemos llevar un recuento de objetos de la clase Estudiante porque CuentaEstudiantes es una variable de clase estática y no una variable de instancia.


sábado, 5 de febrero de 2022

Curso avanzado de C#: Estructuras de datos

Las estructuras de datos, o simplemente las estructuras, son tipos de valores que se pueden usar para almacenar conjuntos de variables relacionados.
Las estructuras comparten algunas similitudes con las clases, pero también tienen ciertas restricciones. El lenguaje C # proporciona numerosos mecanismos para almacenar datos relacionados, como estructuras, clases, Arrays, colecciones, etc. Cada uno tiene un conjunto específico de requisitos y restricciones que dictan cómo o dónde puedes usarlos.

Curso avanzado de C#: Estructuras de datos

Si consideramos que un objeto de la vida real tiene un conjunto de características, podemos entender cómo modelar este objeto utilizando una estructura. Consideramos por ejemplo un estudiante como un objeto del mundo real que deseamos modelar en código. Podríamos considerar usar una clase para esto, pero si pensamos en las características
que queremos modelar consideramos cómo deseamos utilizar la estructura Estudiante en nuestro código.  Para este ejemplo simple, consideramos la estructura estudiante como un medio para ayudar calcular su calificación promedio. Las características a considerar serían:

Nombre
Apellidos
Clase
Nota 1
Nota 2
Nota 3
Nota 4
Nota 5
Media

Utilizaremos un conjunto de características relativamente simples donde limite el número de  notas a solo 5, proporcionaremos un campo para almacenar la media de todas las pruebas, y campos para el nombre del alumno y su clase. También podríamos haber usado una matriz para las calificaciones, pero de momento crearemos esta estructura en el código: 

public struct Estudiante
{
   public string nombre;
    public string apellido;
    public char clase;
    public double nota1;
    public double nota2;
    public double nota3;
    public double nota4;
    public double nota5;
    public double media;
}

La estructura Estudiante creada incluye un conjunto de propiedades representadas por variables de tipo de valor simple, como podemos ver, una estructura es un tipo de valor, pero es un tipo de valor complejo porque puede contener múltiples tipos de valores diferentes como propiedades. 
Para usar esta estructura en el código, necesitamos crear una nueva instancia de ella. No podemos utilizar Estudiante como un nuevo tipo de datos en nuestro código. El siguiente código muestra cómo crear una nueva instancia para la estructura Estudiante: 

// crea una nueva instancia de la estructura estudiante en el código
 Estudiante miEstudiante = new Estudiante();
// crea una nueva instancia de la estructura estudiante sin la instrucción new;
Estudiante miOtroEstudiante;

Después de crear una nueva instancia de la estructura, podemos comenzar a asignar o leer valores de las propiedades declaradas en la estructura. A continuación se muestra la creación de una nueva estructura de tipo Estudiante,  que asigna y lee valores de y hacia las propiedades. Otro pequeño fragmento de código intenta utilizar Estudiante directamente en el código. 

// crea  una nueva instancia de la estructura Estudiante
Estudiante miEstudiante = new Estudiante();
// asigna algunos valores a las propiedades de miEstudiante miEstudiante.nombre = "Pepe";
miEstudiante.nombre = "Juan";
miEstudiante.nota1 = 8;
miEstudiante.nota2 = 9;

Console.Write("Estudiante " + miEstudiante.nombre + " " + miEstudiante.apellido);

Console.Write(" nota " + miEstudiante.nota1 + " en su primer examen. ");
// la instrucción siguiente es incorrecta, no se puede utilizar el tipo directamente
// Visual Studio indicará que se requiere una referencia al objeto  Estudiante.nombre = "Fallo";

Console.ReadLine();  //esta última línea es la más importante pues //detiene la pantalla y nos permite leer el resultado

Las estructuras pueden contener más que solo propiedades. Pueden incluir funciones, constructores, constantes, indexadores, operadores, eventos y tipos anidados y pueden implementar interfaces. Debemos entender El uso de constructores en estructuras porque difieren ligeramente de las clases. Vale la pena señalar los siguientes puntos sobre constructores en estructuras:

Los constructores son opcionales, pero si se incluyen deben contener parámetros. No se permiten constructores por defecto.

Los campos no se pueden inicializar en un cuerpo de estructura.

Los campos se pueden inicializar solo usando el constructor o después de que se haya declarado la estructura. Los miembros privados pueden inicializarse usando solo el constructor.

La creación de un nuevo tipo de estructura sin el nuevo operador no dará lugar a una llamada a un constructor si uno esta presente 

Si nuestra estructura contiene un tipo de referencia (clase) como uno de sus miembros, debemos llamar al el constructor del tipo de referencia explícitamente. 

El siguiente código establece el Nombre del alumno cuando se crea el objeto: 

 // crea una estructura estudiante que utiliza un constructor
 public struct Estudiante
 {
   public string nombre;
    public string apellido;
    private string curso;
    public Estudiante(string nom, string ape, string curso)
    {
      this.nombre = nom;
        this.apellido = ape;
        this.curso = curso;
    }
}

En el código anterior, la estructura se simplifica para mostrar el uso del constructor. Tenemos solo dos campos para el nombre y apellido, y utilizamos el constructor para suministrar esos valores a los campos cuando el objeto se crea con la palabra clave new.

El siguiente fragmento de código de muestra un uso ilegal de un constructor en una estructura. La razón es que si creamos un constructor en una estructura, debemos proporcionar valores para todos los campos de la estructura; si no, se produce un error.

public struct Estudiante
{
   public string nombre;
    public string apellido;
    public char clase;
    public double nota1;
    public double nota2;
    public double nota3;
    public double nota4;
    public double nota5;
    public double media;
    public Estudiante(string nom, string ape)
    {
      this.nombre = nom;
        this.apellido = ape;
    }
}

Como se dijo antes, una estructura puede contener funciones también. Podemos crear funciones o métodos en una estructura para poder realizar alguna acción sobre los miembros de datos que contiene. El siguiente fragmento de código muestra un ejemplo  con un método  para calcular la media de la nota, el constructor se ha eliminado para mantener el ejemplo más limpio. 

// crea una estructura estudiante que utiliza un constructor
// Estructura Estudiante  que contiene un método para calcular la nota media
public struct Estudiante
 {
   public string nombre;
    public string apellido;
    public char clase;
    public double nota1;
    public double nota2;
    public double nota3;
    public double nota4;
    public double nota5;
    public double media;
    public void calcMedia()
    {
      double med = ((nota1 + nota2 + nota3 + nota4 + nota5) / 5);
        this.media = med;
    }
}

Desde una perspectiva de código eficiente, habrá que considerar lo más óptimo entre elegir una estructura o una clase.

La estructura Estudiante es simple y debe crearse como estructura para evitar  sobrecargas innecesarias. Es necesario evaluar los escenarios donde deseamos almacenar un número determinado de objetos, ya sea estructura, clase, colección, matriz, etc. teniendo en cuenta que los tipos se pasan por valor, su consumo de memoria puede crecer bastante rápido. En el caso en el que tuviéramos que rellenar bastantes objetos con muchas referencias a estos objetos, en lugar de una estructura de datos del estudiante podríamos considerar una clase.

Creando estructuras (caso práctico)


Abrimos Visual Studio y creamos una aplicación basada en la consola de C # y le ponemos de nombre EstructuraLibros. La estructura del libro contendrá las siguientes propiedades: 

Título
Categoría
Autor
Número de páginas
página actual
ISBN
estilo de la cubierta
Métodos para pasar páginas, llamados PaginaSiguiente y PaginaAnterior. 

Utilizaremos un constructor para  inicializar las propiedades. Un método principal en la aplicación de consola, crearemos la estructura indicada anteriormente para un libro,  y asignaremos sus propiedades al constructor. Utilizando el método console.WriteLine, generaremos cada propiedad en la ventana de la consola y luego llamaremos a los métodos de página siguiente página o anterior. Estos métodos deberán tener en cuenta sólo la página actual y luego aumentar o decrementar  basándose en el método llamado. 

Solución 

public struct Libro
        {
            public string titulo;
            public string categoria;
            public string autor;
            public int numPaginas;
            public int PaginaActual;
            public double ISBN;
            public string cubierta;
            public Libro(string titulo, string categoria, string autor, int numPaginas, int
            PaginaActual, double isbn, string cubierta)
            {
                this.titulo = titulo;
                this.categoria = categoria;
                this.autor = autor;
                this.numPaginas = numPaginas;
                this.PaginaActual = PaginaActual;
                this.ISBN = isbn;
                this.cubierta = cubierta;
            }
            public void PaginaSiguiente()
            {
                if (PaginaActual != numPaginas)
                {
                    PaginaActual++;
                    Console.WriteLine("La página actual es: " + this.PaginaActual);
                }
                else
                {
                    Console.WriteLine("Fin del libro.");
                }
            }
            public void PaginaAnterior()
            {
                if (PaginaActual != 1)
                {
                    PaginaActual--;
                    Console.WriteLine("La página actual es: " + this.PaginaActual);
                }
                else
                {
                    Console.WriteLine("Comienzo del libro.");

                }
            }
        }

        static void Main(string[] args)
        {
            Libro miLibro = new Libro("El Quijote",
            "Novela", "Miguel de Cervantes", 648, 1, 9999999999, "Tapa blanda");
            Console.WriteLine(miLibro.titulo);
            Console.WriteLine(miLibro.categoria);
            Console.WriteLine(miLibro.autor);
            Console.WriteLine(miLibro.numPaginas);
            Console.WriteLine(miLibro.PaginaActual);
            Console.WriteLine(miLibro.ISBN);
            Console.WriteLine(miLibro.cubierta);
            Console.ReadLine();  //esta última línea es la más importante pues
            //detiene la pantalla y nos permite leer el resultado
            miLibro.PaginaSiguiente();
            miLibro.PaginaAnterior();
        }

Los métodos y propiedades se han declarado como públicos. Las estructuras comparten rasgos similares a las clases para la accesibilidad de los miembros de la estructura public  significa que el código tiene acceso a los miembros directamente y puede asignar valores y leer valores, así como llamar a los métodos. 

Si deseamos controlar el acceso a sus miembros debemos declararlos como private en lugar de public. Esto permitirá crear métodos de acceso para las propiedades.
Los métodos de acceso son métodos que son públicos y proporcionan una interfaz a su estructura. Mediante el uso métodos de acceso, podemos configurar los campos miembro de los datos como privados, lo que evitará la escritura o la lectura ellos directamente de modo que los usuarios de nuestra estructura deben pasar por los métodos de acceso.

En estos métodos, podemos incluir un código para verificar la validez de los datos ingresados. Por ejemplo, ¿que pasa si ahora los desarrolladores utilizan esta estructura de código e intentan ingresar un valor de cadena para el número ISBN? Esto daría lugar a un error en el código. En su lugar, nuestro código dentro de la estructura podría realizar la validación en el valor de entrada y devolver un error al código de llamada si el valor no es correcto.