Está en la página 1de 28

UNIVERSIDAD TÉCNICA LUIS

VARGAS TORRES
TIPOS DE DATOS NATIVOS Y
HEREDADOS POR EL LENGUAJE DE
PROGRAMACION C#

Apellidos y Nombres: Ramirez Reyes Luis Alexander


Carrera: Ingeniería en Tecnología de la Información y Comunicación
Materia: Programación II
Tabla de contenido
1. Resumen 3
2. Introducción 3
3. Objetivos 3
a. Objetivo General 3
b. Objetivos Específicos 3
4. Desarrollo 4-27
5. Conclusiones y Recomendaciones 28
6. Linkografía 28
TIPOS DE DATOS NATIVOS Y HEREDADOS POR EL LENGUAJE DE
PROGRAMACION C#
Resumen
En cuanto lenguaje orientado a objetos, C# admite los conceptos de encapsulación, herencia
y polimorfismo. Una clase puede heredar directamente de una clase primaria e implementar
cualquier número de interfaces.
Los tipos de datos básicos son los tipos de datos más comúnmente utilizados en
programación. Los tipos predefinidos en C# están definidos en el espacio de
nombres System, que es el espacio de nombres más numeroso (e importante) de la
plataforma .NET.

Introducción

Cuando definimos un objeto debemos especificar su tipo. El tipo determina qué valores
puede almacenar ese objeto (clase y rango) y las operaciones que pueden efectuarse con él.

Como cualquier lenguaje de programación, C# proporciona una serie de tipos predefinidos


(int, byte, char, string, ...) y mecanismos para que el usuario cree sus propios tipos
(class y struct).

La estructura de tipos de C# es una gran novedad ya que establece una relación jerárquica
entre éstos, de manera que todos los tipos son clases y se construyen por herencia de la
clase base Objet. Esta particularidad hace que la creación y gestión de tipos de datos en C#
sea una de las principales novedades y potencialidades del lenguaje .

Objetivos
Objetivos Generales
1. Conocer los tipos de datos nativos y heredados por el lenguaje de programación e
Identificar cuáles son sus funciones

Objetivos específicos
1. Comprender el significado de los tipos de datos
2. Estudiar el uso y el funcionamiento de los tipos de datos
3. Aplicar los conocimientos en futuras actividades en clase
Desarrollo

Para representar números enteros de 32 bits con signo se utiliza el tipo de


dato System.Int32 y a la hora de crear un objeto a de este tipo que represente el valor 2 se
usa la siguiente sintaxis:

System.Int32 a = 2;

Al ser un tipo valor no se utiliza el operador new para crear objetos System.Int32, sino que


directamente se indica el literal que representa el valor a crear, con lo que la sintaxis
necesaria para crear entero de este tipo se reduce considerablemente. Es más, dado lo
frecuente que es el uso de este tipo también se ha predefinido en C# el alias int para el
mismo, por lo que la definición de variable anterior queda así de compacta:

int a = 2;

System.Int32 no es el único tipo de dato básico incluido en C#. En el espacio de


nombres System se han incluido los siguientes tipos:

C# Tipo en System Características Símbolo

sbyte System.Sbyte entero, 1 byte con signo

byte System.Byte entero, 1 byte sin signo

short System.Short entero, 2 bytes con signo

ushort System.UShort entero, 2 bytes sin signo

int System.Int32 entero, 4 bytes con signo

uint System.UInt32 entero, 4 bytes sin signo U

long System.Int64 entero, 8 bytes con signo L


ulong System.ULong64 entero, 8 bytes sin signo UL

float System.Single real, IEEE 754, 32 bits F

double System.Double real, IEEE 754, 64 bits D

real, 128 bits (28 dígitos


decimal System.Decimal M
significativos)

bool System.Boolean (Verdad/Falso) 1 byte

char System.Char Carácter Unicode, 2 bytes ´´

Cadenas de caracteres
string System.String ""
Unicode

Cualquier objeto (ningún


object System.Object
tipo concreto)

Los tipos están definidos de manera muy precisa y no son dependientes del compilador o de
la plataforma en la que se usan.

La tabla anterior incorpora la columna Símbolo para indicar cómo debe interpretarse un
literal. Por ejemplo, 28UL debe interpretarse como un entero largo sin signo (ulong).

Todos los tipos enumerados son tipos valor, excepto string (que no debe confundirse con un
vector de caracteres) y Object que son tipos referencia. Recuerde, no obstante, que los
datos string se comportaban como un tipo valor ante la asgnación.

El sistema unificado de tipos. El tipo Object

En C# desaparecen las variables y funciones globales: todo el código y todos los datos de


una aplicación forman parte de objetos que encapsulan datos y código (como ejemplo,
recuerde cómo Main() es un método de una clase). Como en otros lenguajes orientados a
objetos, en C# un tipo puede incluir datos y métodos. De hecho, hasta los tipos básicos
predefinidos incluyen métodos, que, como veremos, heredan de la clase base object, a
partir de la cual se construyen implícita o explícitamente todos los tipos de datos. Por
ejemplo:

int i = 10;
string c = i.ToString();
e incluso:

string c = 10.ToString();
Esta manera por la que podemos manipular los datos básicos refleja la íntima relación entre
C# y la biblioteca de clase de .NET. De hecho, C# compila sus tipos básicos asociándolos a
sus correspondientes en .NET; por ejemplo, hace corresponder al tipo string) con la
clase System.String, al tipo int) con la clase System.Int32, etc. Así, se confirma que todo es
un objeto en C# (por si aún había alguna duda).

Aunque no comentaremos todos los métodos disponibles para los tipos básicos,
destacaremos algunos de ellos.

Todos los tipos tienen un método ToString() que devuelve una cadena (string) que
representa su valor textual.
char tiene propiedades acerca de su contenido (IsLetter, IsNumber, etc.) además
de métodos para realizar conversiones (ToUpper(), ToLower(), etc.)
El tipo básico string dispone, como puede suponerse, de muchos métodos
específicos, aún más que la clase string de la biblioteca estándar de C++.

Algunos métodos estáticos y propiedades particularmente interesantes son:

Los tipos numéricos enteros tipos tienen las


propiedades MinValue y MaxValue (p.e. int.MaxValue).
Los tipos float y double tienen la propiedad Epsilon, que indica el mínimo valor
positivo que puede representarse en un dato de su tipo (p.e. float.Epsilon).
Los tipos float y double tienen definidos los valores NaN (no es un número, no está
definido), PositiveInfinite y NegativeInfinity, valores que son pueden ser
devueltos al realizar ciertos cálculos.
Muchos tipos, incluyendo todos los tipos numéricos, proporcionan el
método Parse() que permite la conversión desde un dato string (p.e. double d =
double.Parse("20.5"))

Conversiones de tipos

Una conversión de tipo (casting) puede ser implícita o explícita.

Implícitas Explícitas

Ocurren automáticamente Requieren un casting

Siempre tienen éxito Pueden fallar

No se pierde información Se puede perder información

int x = 123456;
long y = x; // implicita
short z = (short) x; // explicita (riesgo)
float f1 = 40.0F;
long l1 = (long) f1; // explicita (riesgo por redondeo)
short s1 = (short) l1; // explicita (riesgo por desbordamiento)
int i1 = s1; // implicita, no hay riesgo
uint i2 = (uint) i1; // explicita (riesgo de error por signo)

En C# las conversiones de tipo (tanto implícitas como explícitas) en las que intervengan tipos
definidos por el usuario pueden definirse y particularizarse.

Recordemos que pueden emplearse los operadores checked y unchecked para realizar


conversiones (y operaciones aritméticas) en un contexto verificado: el entorno de
ejecución .NET detecta cualquier situación de desbordamiento y lanza una
excepción OverFlowException si ésta se manifiesta.

El tipo object

Por el sistema unificado de tipos de C#, todo es un objeto. C# tiene predefinido un tipo
referencia llamado object y cualquier tipo (valor o referencia, predefinido o definido por el
usuario) es en última instancia, de tipo object (con otras palabras puede decirse
que hereda todas las características de ese tipo).

El tipo object se basa en System.Object de .NET Framework. Las variables de


tipo object pueden recibir valores de cualquier tipo. Todos los tipos de datos, predefinidos y
definidos por el usuario, heredan de la clase System.Object. La clase Object es la superclase
fundamental de todas las clases de .NET Framework; la raíz de la jerarquía de tipos.

Normalmente, los lenguajes no precisan una clase para declarar la herencia


de Object porque está implícita.

Por ejemplo, dada la siguiente declaración:

object o;
todas estas instrucciones son válidas:

o = 10; // int
o = "Una cadena para el objeto"; // cadena
o = 3.1415926; // double
o = new int [24]; // vector de int
o = false; // boolean
Dado que todas las clases de .NET Framework se derivan de Object, todos los métodos
definidos en la clase Object están disponibles en todos los objetos del sistema. Las clases
derivadas pueden reemplazar, y de hecho reemplazan, algunos de estos métodos, entre los
que se incluyen los siguientes:

public void Object() Inicializa una nueva instancia de la clase Object. Los


constructores llaman a este constructor en clases derivadas, pero éste también
puede utilizarse para crear una instancia de la clase Object directamente.
public bool Equals(object obj) Determina si el objeto especificado es igual al
objeto actual.
protected void Finalize() Realiza operaciones de limpieza antes de que un objeto
sea reclamado automáticamente por el recolector de elementos no utilizados.
public int GetHashCode()  Sirve como función hash para un tipo concreto,
apropiado para su utilización en algoritmos de hash y estructuras de datos
como las tablas hash.
public string ToString() Devuelve un objeto string que representa al objeto actual.
Se emplea para crear una cadena de texto legible para el usuario que describe
una instancia de la clase. La implementación predeterminada devuelve el
nombre completo del tipo del objeto Object.

En el último ejemplo, si cada vez que asignamos un valor


a o ejecutamos Console.WriteLine (o.ToString()) o
simplemente Console.WriteLine (o); obtendremos este resultado:

10
Una cadena para el objeto
3,1415926
System.Int32[]
False

public Type GetType() Obtiene el objeto Type que representa el tipo exacto en


tiempo de ejecución de la instancia actual.

En el último ejemplo, si cada vez que asignamos un valor


a o ejecutamos Console.WriteLine (o.getType()) obtendremos este resultado:

System.Int32
System.String
System.Double
System.Int32[]
System.Boolean

protected object MemberwiseClone() Crea una copia superficial del


objeto Object actual. No se puede reemplazar este método; una clase derivada
debe implementar la interfaz ICloneable si una copia superficial no es
apropiada. MemberwiseClone() está protegido y, por tanto, sólo es accesible a
través de esta clase o de una clase derivada. Una copia superficial crea una
nueva instancia del mismo tipo que el objeto original y, después, copia los
campos no estáticos del objeto original. Si el campo es un tipo de valor, se
realiza una copia bit a bit del campo. Si el campo es un tipo de referencia, la
referencia se copia, pero no se copia el objeto al que se hace referencia; por lo
tanto, la referencia del objeto original y la referencia del punto del duplicado
apuntan al mismo objeto. Por el contrario, una copia profunda de un objeto
duplica todo aquello a lo que hacen referencia los campos del objeto directa o
indirectamente.

Polimorfismo -boxing y unboxing-

Boxing (y su operación inversa, unboxing) permiten tratar a los tipos valor como objetos
(tipo referencia). Los tipos de valor, incluidos los struct y los predefinidos, como int, se
pueden convertir al tipo object (boxing) y desde el tipo object (unboxing).

La posibilidad de realizar boxing permite construir funciones polimórficas: pueden realizar


una operación sobre un objeto sin conocer su tipo concreto.

void Polim(object o)
{
Console.WriteLine(o.ToString());
}
...
Polim(42);
Polim("abcd");
Polim(12.345678901234M);
Polim(new Point(23,45));
Boxing

Boxing es una conversión implícita de un tipo valor al tipo object. Cuando se realiza boxing
de un valor, se asigna una instancia de objeto y se copia el valor en el nuevo objeto.

Por ejemplo, considere la siguiente declaración de una variable de tipo de valor:

int i = 123;
La siguiente instrucción aplica implícitamente la operación de boxing sobre la variable i:

object o = i;
El resultado de esta instrucción es crear un objeto o en la pila que hace referencia a un valor
del tipo int alojado en el heap. Este valor es una copia del valor del tipo de valor asignado a
la variable i. La diferencia entre las dos variables, i y o se muestra en la siguiente figura:
En definitiva, el efecto del boxing es el de cualquier otro tipo de casting pero:

el contenido de la variable se copia al heap


se crea una referencia a ésta copia

Aunque no es necesario, también es posible realizar el boxing explícitamente como en el


siguiente ejemplo:

int i = 123;
object o = (object) i;

El siguiente ejemplo convierte una variable entera i a un objeto o mediante boxing. A


continuación, el valor almacenado en la variable i se cambia de 123 a 456. El ejemplo
muestra que el objeto mantiene la copia original del contenido, 123.

// Boxing de una variable int


using System;
class TestBoxing
{
public static void Main()
{
int i = 123;
object o = i; // boxing implicito
i = 456; // Modifica el valor de i
Console.WriteLine("Valor (tipo valor) = {0}", i);
Console.WriteLine("Valor (tipo object)= {0}", o);
}
}
El resultado de su ejecución es:

Valor (tipo valor) = 456


Valor (tipo object)= 123
Unboxing

Una vez que se ha hecho boxing sobre un dato y disponemos de un object no puede hacerse
demasiado con él ya que no pueden emplearse métodos o propiedades del tipo original: el
compilador no puede conocer el tipo original sobre el que se hizo boxing.

string s1 = "Hola";
object o = s1; // boxing
...
if (o.Lenght > 0) // ERROR

Una vez que se ha hecho boxing puede deshacerse la conversión (unboxing) haciendo
casting explícito al tipo de dato inicial.

string s2 = (string) o; // unboxing

Afortunadamente es posible conocer el tipo, de manera que si la conversión no es posible se


lanza una excepción. Pueden utilizarse los operadores is y as para determinar la corrección
de la conversión:

string s2;
if (o is string)
s2 = (string) o; // unboxing
o alternativamente:

string s2 = o as string; // conversion


if (s2 != null) // OK, la conversion funciono
Conclusiones

Ventajas del sistema unificado de tipos: las colecciones funcionan sobre cualquier tipo.

Hashtable t = new Hashtable();

t.Add(0, "zero");
t.Add(1, "one");
t.Add(2, "two");

string s = string.Format("Your total was {0} on {1}", total, date);

Desventajas del sistema unificado de tipos: Eficiencia.

La necesidad de utilizar boxing disminuirá cuando el CLR permita genéricos (algo similar a


los templates en C++).
Cadenas de caracteres

Una cadena de caracteres no es más que una secuencia de caracteres Unicode. En C# se


representan mediante objetos del tipo string, que no es más que un alias del
tipo System.String incluido en la BCL.

El tipo string es un tipo referencia. Representa una serie de caracteres inmutable. Se dice


que una instancia de String es inmutable porque no se puede modificar su valor una vez
creada. Los métodos que aparentemente modifican una cadena devuelven en realidad una
cadena nueva que contiene la modificación.

Recuerde la particularidad de este tipo sobre el operador de asignación: su funcionamiento


es como si fuera un tipo valor. Este es, realmente, el funcionamiento obtenido con el
método Copy(). Si no se desea obtener una copia (independiente) sino una copia en el
sentido de un tipo valor emplee el método Clone().

Puede accederse a cada uno de los caracteres de la cadena mediante índice, como ocurre
habitualmente en otros lenguajes, siendo la primera posición asociada al índice cero. Este
acceso, no obstante, sólo está permitido para lectura.

string s = "!!Hola, mundo!!";;


for (int i=0; i < s.Length; i++)
Console.Write (s[i]);
Este ejemplo muestra todos los caracteres que componen la cadena, uno a uno. El ciclo
realiza tantas iteraciones como número de caracteres forman la cadena (su longitud) que se
consulta usando la propiedad Length.

Por definición, un objeto String, incluida la cadena vacía (""), es mayor que una referencia
nula y dos referencias nulas son iguales entre sí. El carácter null se define como el
hexadecimal 0x00. Puede consultarse si una cadena es vacía empleando la propiedad
estática (sólo lectura) Empty: el valor de este campo es la cadena de longitud cero o cadena
vacía, "". Una cadena vacía no es igual que una cadena cuyo valor sea null.

Los procedimientos de comparación y de búsqueda distinguen mayúsculas de minúsculas de


forma predeterminada. Pueden emplearse los métodos Compare() y Equals() para realizar
referencias combinadas y comparaciones entre valores de instancias de Object y String. Los
operadores relacionales == y != se implementan con el método Equals().

El método Concat() concatena una o más instancias de String o las representaciones de


tipo String de los valores de una o más instancias de Object. El operador + está
sobrecargado para realizar la concatenación. Por ejemplo, el siguiente código:

string s1 = "Esto es una cadena... como en C++";


string s2 = "Esto es una cadena... ";
string s3 = "como en C++";
string s4 = s2 + s3;
string s5 = String.Concat(s2, s3);
Console.WriteLine ("s1 = {0}", s1);
Console.WriteLine ("s4 = {0}", s4);
Console.WriteLine ("s5 = {0}", s5);

if ((s1 == s4) && (s1.Equals(s5)))


Console.WriteLine ("s1 == s4 == s5");
produce este resultado:

s1 = Esto es una cadena... como en C++


s4 = Esto es una cadena... como en C++
s5 = Esto es una cadena... como en C++
s1 == s4 == s5

Un método particularmente útil es Split(), que devuelve un vector de cadenas resultante de


"partir" la cadena sobre la que se aplica en palabras:

string linea;
string [] palabras;
...
palabras = linea.Split (null); // null indica dividir por espacios

En definitiva, la clase String incluye numerosos métodos, que pueden emplease para:

Realizar búsquedas en cadenas de caracteres: IndexOf(), LastIndexOf(),


StartsWith(), EndsWith()
Eliminar e insertar espacios en blanco: Trim(), PadLeft(),
PadRight()
Manipular subcadenas: Insert(), Remove(), Replace(), Substring(),
Join()
Modificar cadenas de caracteres: ToLower(), ToUpper(),
Format() (al estilo del printf de C, pero seguro).

Vectores y matrices

Un vector (matriz) en C# es radicalmente diferente a un vector (matriz) en C++: más que una
colección de variables que comparten un nombre y accesibles por índice, en C# se trata de
una instancia de la clase System.Array, y en consecuencia se trata de una colección que se
almacena en el heap y que está bajo el control del gestor de memoria.

Todas las tablas que definamos, sea cual sea el tipo de elementos que contengan, son
objetos que derivan de System.Array. Ese espacio de nombres proporciona métodos para la
creación, manipulación, búsqueda y ordenación de matrices, por lo tanto, sirve como clase
base para todas las matrices de la CLR (Common Language Runtime).

En C# las tablas pueden ser multidimensionales, se accede a los elementos por índice,
siendo el índice inicial de cada dimensión 0.
Siempre se comprueba que se esté accediendo dentro de los límites. Si se intenta acceder a
un elemento de un vector (matriz) especificando un índice fuera del rango, se detecta en
tiempo de ejecución y se lanza una excepción IndexOutOfBoundsException.

La sintaxis es ligeramente distinta a la del C++ porque las tablas son objetos de tipo
referencia:

double [] array; // Declara un a referencia


// (no se instancia ningún objeto)
array = new double[10]; // Instancia un objeto de la clase
// System.Array y le asigna 10 casillas.

que combinadas resulta en (lo habitual):

double [] array = new double[10];

El tamaño del vector se determina cuando se instancia, no es parte de la


declaración.
 string [] texto; // OK
 string [10] texto; // Error
La declaración emplea los paréntesis vacíos [ ] entre el tipo y el nombre para
determinar el número de dimensiones (rango).
 string [] Mat1D; // 1 dimension
 string [,] Mat2D; // 2 dimensiones
 string [,,] Mat3D; // 3 dimensiones
 string [,,,] Mat4D; // 4 dimensiones
 ......
En C# el rango es parte del tipo (es obligatorio en la declaración). El número de
elementos no lo es (está asociado a la instancia concreta).

Otros ejemplos de declaración de vectores:

string[] a = new string[10]; // "a" es un vector de 10 cadenas


int[] primes = new int[9]; // "primes" es un vector de 9 enteros

Un vector puede inicializarse a la misma vez que se declara. Las tres definiciones siguientes
son equivalentes:

int[] prime1 = new int[10] {1,2,3,5,7,11,13,17,19,23};


int[] prime2 = new int[] {1,2,3,5,7,11,13,17,19,23};
int[] prime3 = {1,2,3,5,7,11,13,17,19,23};

Los vectores pueden dimensionarse dinámicamente (en tiempo de ejecución). Por ejemplo,
el siguiente código:

Console.Write ("Num. casillas: ");


string strTam = Console.ReadLine();
int tam = Convert.ToInt32(strTam);

int[] VecDin = new int[tam];


for (int i=0; i<tam; i++) VecDin[i]=i;

Console.Write ("Contenido de VecDin = ");


foreach (int i in VecDin)
Console.Write(i + ", ");

Console.ReadLine();

produce este resultado:

Num. casillas: 6
Contenido de VecDin = 0, 1, 2, 3, 4, 5,

Esta facilidad es una gran ventaja, aunque una vez que el constructor actúa y se crea una
instancia de la clase System.Array  no es posible redimensionarlo. Si se desea una estructura
de datos con esta funcionalidad debe emplearse una estructura colección disponible
en System.Collections  (por elemplo, System.Collections.ArrayList).

En el siguiente ejemplo observe cómo el vector palabras se declara como un vector


de string. Se instancia y se inicia después de la ejecución del método Split(), que devuelve un
vector de string.

string frase = "Esto es una prueba de particion";


string [] palabras; // vector de cadenas (no tiene tamaño
asignado)

palabras = frase.Split (null);

Console.WriteLine ("Frase = {0}", frase);


Console.WriteLine ("Hay = {0} palabras", palabras.Length);
for (int i=0; i < palabras.Length; i++)
Console.WriteLine (" Palabra {0} = {1}", i, palabras[i]);
El resultado es:

Frase = Esto es una prueba de particion


Hay = 6 palabras
Palabra 0 = Esto
Palabra 1 = es
Palabra 2 = una
Palabra 3 = prueba
Palabra 4 = de
Palabra 5 = particion
Matrices

Las diferencias son importantes respecto a C++ ya que C# permite tanto matrices


rectangulares (todas las filas tienen el mismo número de columnas) como matrices
dentadas o a jirones.

int [,] array2D; // Declara un a referencia


// (no se instancia ningún objeto)
array2D = new int [2,3]; // Instancia un objeto de la clase
// System.Array y le asigna 6 casillas
// (2 filas con 3 columnas cada una)

que combinadas (y con inicialización) podría quedar:

int [,] array2D = new int [2,3] {{1,0,4}, {3,2,5}};

El número de dimensiones puede ser, lógicamente, mayor que dos:

int [,,] array3D = new int [2,3,2];

El acceso se realiza con el operador habitual [ ], aunque el recorrido se simplica y clarifica en


C# con el ciclo foreach:
for (int i= 0; i< vector1.Length; i++)
vector1[i] = vector2[i];
...
foreach (float valor in vector2)
Console.Wtite (valor);

Una matriz dentada no es más que una tabla cuyos elementos son a su vez tablas,
pudiéndose así anidar cualquier número de tablas. Cada tabla puede tener un número
propio de casillas. En el siguiente ejemplo se pide el número de filas, y para cada fila, el
número de casillas de ésa.

Console.WriteLine ("Introduzca las dimensiones: ");

// Peticion de numero de filas (num. vectores)


Console.Write ("Num. Filas: ");
string strFils = Console.ReadLine();
int Fils = Convert.ToInt32(strFils);

// Declaracion de la tabla dentada: el numero de


// casillas de cada fila es deconocido.
int[][] TablaDentada = new int[Fils][];

// Peticion del numero de columnas de cada vector


for (int f=0; f<Fils; f++)
{
Console.Write ("Num. Cols. de fila {0}: ", f);
string strCols = Console.ReadLine();
int Cols = Convert.ToInt32(strCols);

// Peticion de memoria para cada fila


TablaDentada[f] = new int[Cols];
}

// Rellenar todas las casillas de la tabla dentada


for (int f=0; f<TablaDentada.Length; f++)
for (int c=0; c<TablaDentada[f].Length; c++)
TablaDentada[f][c]=((f+1)*10)+(c+1);

// Mostrar resultado
Console.WriteLine ("Contenido de la matriz: ");
for (int f=0; f<TablaDentada.Length; f++)
{
for (int c=0; c<TablaDentada[f].Length; c++)
Console.Write (TablaDentada[f][c] + " ");
Console.WriteLine();
}

Console.ReadLine();

El resultado es:

Introduzca las dimensiones:


Num. Filas: 4
Num. Cols. de fila 0: 2
Num. Cols. de fila 1: 5
Num. Cols. de fila 2: 3
Num. Cols. de fila 3: 7
Contenido de la matriz:
11 12
21 22 23 24 25
31 32 33
41 42 43 44 45 46 47
Estructuras

Una estructura (struct) se emplea para definir nuevos tipos de datos. Los nuevos tipos así
definidos son tipos valor (se almacenan en la pila).

La sintaxis para la definición de estructuras es similar a la empleada para las clases (se
emplea la palabra reservada struct en lugar de class). No obstante,

La herencia y aspectos relacionados (p.e. métodos virtuales, métodos abstractos)


no se admiten en los struct.
No se puede especificar (explícitamente) un constructor sin parámetros. Los
datos struct tienen un constructor sin parámetros predefinido y no puede
reemplazarse, sólo se permiten constructores con parámetros. En este sentido
son diferentes a los struct en C++.
En el caso de especificar algún constructor con parámetros deberíamos
asegurarnos de iniciar todos los campos.

En el siguiente ejemplo se proporciona un único constructor con parámetros:

public struct Point


{
public int x, y;

public Point(int x, int y)


{
this.x = x;
this.y = y;
}
public string Valor()
{
return ("[" + this.x + "," + this.y + "]");
}
}

Sobre esta declaración de tipo Point, observe estas declaraciones de datos Point:

Point p = new Point(2,5);


Point p2 = new Point();
Ambas crean una instancia de la clase Point en la pila y asigna los valores oportunos a los
campos del struct con el constructor:

En el primer caso actúa el constructor suministrado en la implementación del tipo.


En el segundo actúa el constructor por defecto, que inicia los campos numéricos a
cero, y los de tipo referencia a null.

Puede comprobarse fácilmente:

Console.WriteLine ("p = " + p.Valor());


Console.WriteLine ("p2 = " + p2.Valor());
produce como resultado:

p = [2,5]
p2 = [0,0]

En cambio, la siguiente declaración:

Point p3;
crea una instancia de la clase Point en la pila sin iniciar (cuidado: no inicia p3 a null, no es un
tipo referencia). Por lo tanto, la instrucción:

Console.WriteLine ("p3 = " + p3.Valor());


produce un error de compilación, al intentar usar una variable no asignada.

Observe las siguientes operaciones con struct. Su comportamiento es previsible:

p.x += 100;
int px = p.x; // p.x==102
p3.x = px; // p3.x==102
p2 = p; // p2.x==102, p2.y==5
p2.x += 100; // p2.x==202, p2.y==5
p3.y = p.y + p2.y; // p3.y==10

Console.WriteLine ("p = " + p.Valor());


Console.WriteLine ("p2 = " + p2.Valor());
Console.WriteLine ("p3 = " + p3.Valor());
el resultado es:
p = [102,5]
p2 = [202,5]
p3 = [102,10]
Enumeraciones

Una enumeración o tipo enumerado es un tipo especial de estructura en la que los literales


de los valores que pueden tomar sus objetos se indican explícitamente al definirla.

La sintaxis es muy parecida a la empleada en C++:

enum State { Off, On };


aunque el punto y coma final es opcional ya que una enumeración es una definición de
un struct y ésta no requiere el punto y coma:

enum State { Off, On }

Al igual que en C++ y en C, C# numera a los elementos de la enumeración con valores


enteros sucesivos, asignando el valor 0 al primero de la enumeración, a menos que se
especifique explícitamente otra asignación:

enum Tamanio {
Pequeño = 1,
Mediano = 3,
Grande = 5
Inmenso
}
En el ejemplo, el valor asociado a Inmenso es 6.

Para acceder a los elementos de una enumeración debe cualificarse completamente:

Tamanio Ancho = Tamanio.Grande;


lo que refleja que cada enumeración es, en última instancia, un struct.

Si no se declara ningún tipo subyacente de forma explícita, se utiliza Int32. En el siguiente


ejemplo se especifica el tipo base byte:

enum Color: byte {


Red = 1,
Green = 2,
Blue = 4,
Black = 0,
White = Red | Green | Blue
}

La clase Enum (System.Enum) proporciona la clase base para las enumeraciones.


Proporciona métodos que permiten comparar instancias de esta clase, convertir el valor de
una instancia en su representación de cadena, convertir la representación de cadena de un
número en una instancia de esta clase y crear una instancia de una enumeración y valor
especificados. Por ejemplo:

Color c1 = Color.Black;
Console.WriteLine((int) c1); // 0
Console.WriteLine(c1); // Black
Console.WriteLine(c1.ToString()); // Black

Pueden convertirse explícitamente en su valor entero (como en C). Admiten determinados


operadores aritméticos (+,-,++,--) y lógicos a nivel de bits (&,|,^,~). ejemplo:

Color c2 = Color.White;

Console.WriteLine((int) c2); // 7
Console.WriteLine(c2.ToString()); // White

Otro ejemplo más complejo:

enum ModArchivo
{
Lectura = 1,
Escritura = 2,
Oculto = 4,
Sistema = 8
}
...
ModArchivo st = ModArchivo.Lectura | ModArchivo.Escritura;
...
Console.WriteLine (st.ToString("F")); // Lectura, Escritura
Console.WriteLine (Enum.Format(typeof(ModArchivo), st, "G")); // 3
Console.WriteLine (Enum.Format(typeof(ModArchivo), st, "X")); // 00000003
Console.WriteLine (Enum.Format(typeof(ModArchivo), st, "D")); // 3
[ CITATION Fra17 \l 12298 ]
Clases y objetos

Las clases son los tipos más fundamentales de C#. Una clase es una estructura de datos que
combina estados (campos) y acciones (métodos y otros miembros de función) en una sola
unidad. Una clase proporciona una definición para instancias de la clase, también conocidas
como objetos. Las clases admiten herencia y polimorfismo, mecanismos por los que
las clases derivadas pueden extender y especializar clases base.

Las clases nuevas se crean mediante declaraciones de clase. Una declaración de clase


comienza con un encabezado. El encabezado especifica lo siguiente:

 Atributos y modificadores de la clase


 Nombre de la clase
 Clase base (al heredar de una clase base)
 Interfaces implementadas por la clase

Al encabezado le sigue el cuerpo de la clase, que consta de una lista de declaraciones de


miembros escritas entre los delimitadores { y }.

En el código siguiente se muestra una declaración de una clase simple denominada Point:

C#Copiar
public class Point
{
public int X { get; }
public int Y { get; }

public Point(int x, int y) => (X, Y) = (x, y);


}

Las instancias de clases se crean mediante el operador new, que asigna memoria para una
nueva instancia, invoca un constructor para inicializar la instancia y devuelve una referencia
a la instancia. Las instrucciones siguientes crean dos objetos Point y almacenan las
referencias en esos objetos en dos variables:

C#Copiar
var p1 = new Point(0, 0);
var p2 = new Point(10, 20);

La memoria ocupada por un objeto se reclama automáticamente cuando el objeto ya no es


accesible. En C#, no es necesario ni posible desasignar objetos de forma explícita.

Parámetros de tipo

Las clases genéricas definen *parámetros de tipo _. Los parámetros de tipo son una lista de
nombres de parámetros de tipo entre paréntesis angulares. Los parámetros de tipo siguen
el nombre de la clase. Los parámetros de tipo pueden usarse luego en el cuerpo de las
declaraciones de clase para definir a los miembros de la clase. En el ejemplo siguiente, los
parámetros de tipo de Pair son TFirst y TSecond:

C#Copiar
public class Pair<TFirst, TSecond>
{
public TFirst First { get; }
public TSecond Second { get; }

public Pair(TFirst first, TSecond second) =>


(First, Second) = (first, second);
}

Un tipo de clase que se declara para tomar parámetros de tipo se denomina _tipo de clase
genérica*. Los tipos de estructura, interfaz y delegado también pueden ser
genéricos. Cuando se usa la clase genérica, se deben proporcionar argumentos de tipo para
cada uno de los parámetros de tipo:

C#Copiar
var pair = new Pair<int, string>(1, "two");
int i = pair.First; // TFirst int
string s = pair.Second; // TSecond string

Un tipo genérico con argumentos de tipo proporcionado,


como Pair<int,string> anteriormente, se conoce como tipo construido.

Clases base

Una declaración de clase puede especificar una clase base. Tras el nombre de clase y los
parámetros de tipo, agregue un signo de dos puntos y el nombre de la clase base. Omitir
una especificación de la clase base es igual que derivarla del tipo object. En el ejemplo
siguiente, la clase base de Point3D es Point. En el primer ejemplo, la clase base
de Point es object:

C#Copiar
public class Point3D : Point
{
public int Z { get; set; }

public Point3D(int x, int y, int z) : base(x, y)


{
Z = z;
}
}

Una clase hereda a los miembros de su clase base. La herencia significa que una clase
contiene implícitamente casi todos los miembros de su clase base. Una clase no hereda la
instancia, los constructores estáticos ni el finalizador. Una clase derivada puede agregar
nuevos miembros a aquellos de los que hereda, pero no puede quitar la definición de un
miembro heredado. En el ejemplo anterior, Point3D hereda los miembros X y Y de Point, y
cada instancia de Point3D contiene tres miembros: X, Y y Z.
Existe una conversión implícita de un tipo de clase a cualquiera de sus tipos de clase
base. Una variable de un tipo de clase puede hacer referencia a una instancia de esa clase o
a una instancia de cualquier clase derivada. Por ejemplo, dadas las declaraciones de clase
anteriores, una variable de tipo Point puede hacer referencia a una instancia
de Point o Point3D:

C#Copiar
Point a = new Point(10, 20);
Point b = new Point3D(10, 20, 30);
Estructuras

Las clases definen tipos que admiten la herencia y el polimorfismo. Permiten crear


comportamientos sofisticados basados en jerarquías de clases derivadas. Por el contrario,
los tipos *struct _ son tipos más sencillos cuyo propósito principal es almacenar valores de
datos. Dichos tipos struct no pueden declarar un tipo base; se derivan implícitamente
de System.ValueType. No se pueden derivar otros tipos de struct a partir de un tipo
de struct. Están sellados implícitamente.

C#Copiar
public struct Point
{
public double X { get; }
public double Y { get; }

public Point(double x, double y) => (X, Y) = (x, y);


}
Interfaces

Una interfaz define un contrato que se puede implementar mediante clases y


estructuras. Una interfaz puede contener métodos, propiedades, eventos e
indexadores. Normalmente, una interfaz no proporciona implementaciones de los
miembros que define, sino que simplemente especifica los miembros que se deben
proporcionar mediante clases o estructuras que implementan la interfaz.

Las interfaces pueden usar la herencia múltiple. En el ejemplo siguiente, la


interfaz IComboBox hereda de ITextBox y IListBox.

C#Copiar
interface IControl
{
void Paint();
}

interface ITextBox : IControl


{
void SetText(string text);
}

interface IListBox : IControl


{
void SetItems(string[] items);
}

interface IComboBox : ITextBox, IListBox { }

Las clases y los structs pueden implementar varias interfaces. En el ejemplo siguiente, la
clase EditBox implementa IControl y IDataBound.

C#Copiar
interface IDataBound
{
void Bind(Binder b);
}

public class EditBox : IControl, IDataBound


{
public void Paint() { }
public void Bind(Binder b) { }
}

Cuando una clase o un struct implementan una interfaz determinada, las instancias de esa
clase o struct se pueden convertir implícitamente a ese tipo de interfaz. Por ejemplo

C#Copiar
EditBox editBox = new EditBox();
IControl control = editBox;
IDataBound dataBound = editBox;
Enumeraciones

Un tipo de enumeración define un conjunto de valores constantes. En el


elemento enum siguiente se declaran constantes que definen diferentes verduras de raíz:

C#Copiar
public enum SomeRootVegetable
{
HorseRadish,
Radish,
Turnip
}
También puede definir un elemento enum que se usará de forma combinada como
marcas. La declaración siguiente declara un conjunto de marcas para las cuatro
estaciones. Se puede aplicar cualquier combinación de estaciones, incluido un valor All que
incluya todas las estaciones:

C#Copiar
[Flags]
public enum Seasons
{
None = 0,
Summer = 1,
Autumn = 2,
Winter = 4,
Spring = 8,
All = Summer | Autumn | Winter | Spring
}

En el ejemplo siguiente se muestran las declaraciones de ambas enumeraciones anteriores:

C#Copiar
var turnip = SomeRootVegetable.Turnip;

var spring = Seasons.Spring;


var startingOnEquinox = Seasons.Spring | Seasons.Autumn;
var theYear = Seasons.All;
Tipos que aceptan valores NULL

Las variables de cualquier tipo se pueden declarar como que no aceptan valores NULL o que
admiten un valor NULL. Una variable que acepta valores NULL puede contener un
valor null adicional que no indica valor alguno. Los tipos de valor que aceptan valores NULL
(estructuras o enumeraciones) se representan mediante System.Nullable<T>. Los tipos de
referencia que no aceptan valores NULL y los que sí aceptan valores NULL se representan
mediante el tipo de referencia subyacente. La distinción se representa mediante metadatos
leídos por el compilador y algunas bibliotecas. El compilador proporciona advertencias
cuando se desreferencian las referencias que aceptan valores NULL sin comprobar primero
su valor con null. El compilador también proporciona advertencias cuando las referencias
que no aceptan valores NULL se asignan a un valor que puede ser null. En el ejemplo
siguiente se declara un elemento int que admite un valor NULL, y que se inicializa en null. A
continuación, establece el valor en 5. Muestra el mismo concepto con una cadena que
admite un valor NULL. Para más información, consulte Tipos de valor que admiten un valor
NULL y Tipos de referencia que aceptan valores NULL.

C#Copiar
int? optionalInt = default;
optionalInt = 5;
string? optionalText = default;
optionalText = "Hello World.";
Tuplas

C# admite _ tuplas*, lo que proporciona una sintaxis concisa para agrupar varios elementos
de datos en una estructura de datos ligera. Puede crear una instancia de una tupla
declarando los tipos y los nombres de los miembros entre ( y ), como se muestra en el
ejemplo siguiente:

C#Copiar
(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5.
[ CITATION Bil21 \l 12298 ]

Conclusión
Los tipos de datos nativos y heredados nos da la facilidad de que C# admita los conceptos de
encapsulación, herencia y polimorfismo. Una clase puede heredar directamente de una
clase primaria e implementar cualquier número de interfaces.

Linkografía
Arturo, C. (06 de febrero de 2020). academia. Obtenido de academia:
https://www.academia.edu/22438329/PALABRAS_RESERVADAS_PARA_C_

Bon, F. C. (2017). Elvex. Obtenido de Elvex: http://elvex.ugr.es/decsai/csharp/language/intro.xml

Wagner, B. (17 de Marzo de 2021). microsoft. Obtenido de microsoft: https://docs.microsoft.com/es-


es/dotnet/csharp/language-reference/keywords/

También podría gustarte