La creación de tipos
Introducción
Las clases representan la mayoría de los tipos por referencia. La definición más sencilla de una clase es:
class MiClase
{
}
A medida que se construye una clase, se van añadiendo elementos:
-
Los miembros (métodos, propiedades, indexadores, eventos...) se encierran entre llaves.
-
Los atributos y modificadores de clase, como el nivel de acceso, se sitúan antes de la palabra clave class.
-
La herencia e implementaciones de interfaces se sitúan después del nombre de la clase.
Los niveles de acceso
Los niveles de acceso permiten definir cómo se podrá hacer la instanciación de los tipos y la llamada de los métodos. El nivel de acceso se define mediante las palabras clave situadas antes de la declaración de la clase o del miembro. La tabla siguiente presenta los modificadores de acceso disponibles:
Modificador de acceso |
Descripción |
public |
Autoriza el acceso para todos los tipos del ensamblado y fuera del ensamblado. |
private |
Autoriza el acceso solo para los otros miembros del tipo. |
internal |
Autoriza el acceso para todos los tipos del ensamblado únicamente. |
protected |
Autoriza el acceso únicamente para los otros miembros del tipo o para los tipos heredados de éste, incluso fuera del ensamblado. |
protected internal |
Autoriza el acceso únicamente para los otros miembros del tipo o solo para los tipos que heredan de éste, en el ensamblado. |
Si no se indica ningún modificador de acceso para un miembro, se considera como private. Una clase o una estructura sin modificador de acceso se considera como public.
Los miembros nunca podrán exceder su nivel de acceso más allá del tipo que contienen. Esto significa que incluso si un miembro se marca con el modificador de acceso public, y la clase en la que se encuentra está marcada como internal, el miembro solo será accesible para los tipos del ensamblado:
internal class MiClase
{ ...
Las estructuras
Las estructuras son muy parecidas a las clases. La principal diferencia es que una estructura es un tipo por valor, mientras que una clase es un tipo por referencia. Las estructuras se usan en lugar de las clases cuando la semántica exige un tipo por valor. Una estructura no soporta la herencia. Pueden tener todos los miembros de una clase, excepto un constructor sin parámetros, un destructor y miembros virtuales.
A continuación, se muestra un ejemplo de definición de una estructura:
struct PuntoGeografico
{
double Longitud;
double Latitud;
}
La declaración y la instanciación de una ocurrencia de la estructura se hacen como para una clase. Un constructor sin parámetro existe implícitamente:
PuntoGeografico g = new PuntoGeografico();
Es posible sobrecargar este constructor para inicializar los miembros de la estructura, pero debe tenerse en cuenta que, al usar la palabra clave default, la inicialización asignará los valores predefinidos para los tipos de los miembros (en el siguiente ejemplo, 0 para los tipos double):
public PuntoGeografico()
{
this.Longitud = 1;
this.Latitud = 2;
}
var g1 = new PuntoGeografico();
// g1.Longitud = 1
// g1.Latitud = 2
var g2 = default(PuntoGeografico);
//...
Las clases
1. Los campos
Un campo es una variable que es un miembro de la clase. Se puede tratar de un tipo por valor o de un tipo por referencia.
En la raíz del proyecto, cree una carpeta llamada Library y cree una nueva clase llamada Project. Añada los siguientes campos:
public class Project
{
protected string filename = "sin titulo.smpx", path;
protected DataTable data = new DataTable();
protected bool hasChanged;
}
Los campos se pueden inicializar en el momento de la declaración. Un campo que se inicializa explícitamente recibirá los valores de manera predeterminada, según su tipo. La inicialización de los campos se hace antes de la ejecución del constructor de la clase.
También es posible declarar e inicializar varios campos en una única instrucción, si no tienen el mismo nivel de acceso y el mismo tipo:
private string filename = "sin titulo.smpx", path;
La palabra clave readonly permite especificar que un campo será accesible en modo de solo lectura. Solo se podrá asignar durante la declaración o durante la instanciación, dentro del constructor:
public readonly int i = 1;
2. Las propiedades
Las propiedades se parecen a los campos, ya que podemos acceder a ellas del mismo modo, pero su lógica interna las aproxima más a los métodos.
Una propiedad se declara del mismo modo que un campo, añadiendo los bloques get y set. Estos dos bloques se llaman descriptores de acceso, el descriptor de acceso get se ejecuta cuando se lee la propiedad y debe devolver un valor del tipo de la propiedad. El descriptor de acceso set se ejecuta cuando la propiedad se asigna. Se suministra un parámetro implícito, accesible con la palabra clave value del mismo tipo que la propiedad.
Use las herramientas de refactorización de Visual Studio ([Ctrl] R, E con el cursor encima del nombre de un campo) para administrar las propiedades de los campos creados anteriormente en la clase Project. El código que se genera es el siguiente:
public string Filename
{
get { return filename; }
set { filename = value; }
}
public string Path
{
...
Los records
Un record (registro) representa un objeto inmutable. Es suficiente con definir la clase con la palabra clave record y los argumentos:
public record Persona(string Nombre, string Apellido);
Durante la compilación, esta línea se traduce en una clase con dos propiedades inmutables, un constructor con dos parámetros, un constructor por copia y un destructor.
La creación de instancias de un registro se realiza de la misma manera que una clase. Simplemente use el constructor provisto:
Persona p = new Persona("Ángel", "Sánchez");
También es posible utilizar el constructor mediante copia con la palabra clave with:
Persona e = p with { Nombre = "Juan" };
Si no se especifica el tipo de registro (class o struct), el compilador creará por defecto un record de tipo referencia, es decir, una clase. Para crear un record de tipo valor, esto es, una estructura, la palabra clave record debe combinarse con la palabra clave struct:
public record struct Vector(int X, int y);
Para mayor claridad, se puede especificar explícitamente la palabra clave class:
public record class Persona(string Nombre, string Apellido) ;