Inicio Blog

Tipos de Valor y Tipos de Referencia

0

El tipo de una variable define cuanta memoria es requerida para almacerar sus datos. Tambien determina en que lugar de la memoria seran almacenados, la Pila o el Monticulo.

Los tipos estan divididos en dos categorias:

  1. Tipos de Valor.
  2. Tipos de Referencia.
  • Los tipos de valor requieren solo un segmento de memoria, el cual almacenara los datos.
  • Los tipos de referencia requieren dos segmentos de memoria:
    • El primero almacena los datos, y esta localizado en el monticulo.
    • El segundo es una referencia a donde los datos estan almacenados en el monticulo.

Sin embargo existen casos en que la referencia puede ser almacenada en el monticulo y no en la pila.

 

Existen dos clases de tipos de valor:

  1. Structs.
  2. Enumeraciones.

 

La Pila (The Stack) y el Montículo (The Heap) en C# .NET

0

Cuando un programa se va a ejecutar, se reserva un espacio de memoria para almacenar su entorno, así como también los datos que va a utilizar. La cantidad de memoria que será requerida, donde y como será almacenada, dependerá del tipo de dato a declarar.

Al momento de requerir un espacio de memoria, se puden usar dos áreas distintas, las cuales son administradas de forma diferente. Estas dos áreas de memoria se llaman: La Pila (The Stack) y El Montículo o Montón (The Heap). Cada una con diferentes propósitos.

The Stack

La Pila es un arreglo de memoria que esta ordenado mediante el método LIFO (Last-in, First-out), donde el último elemento en entrar es el primero en salir.

Las características generales de la Pila son las siguientes:

  1. Los datos solamente pueden ser añadidos o removidos desde la parte superior de la pila.
  2. Agregar un elemento a la pila se denomina Pushing.
  3. Remover un elemento de la pila se denomina Popping.
  4. La pila almacena:
    1. El entorno de ejecución del programa actual.
    2. Parámetros pasados a los métodos.
    3. Los valores de ciertos tipos de variables.

Otra forma de describir a La Pila es relacionándola con la forma en que está organizada una pila de cartones, los cuales se colocan uno encima del otro.

Por ejemplo, cuando se llama a un método, cada parámetro se coloca dentro de un cartón y luego el mismo se coloca en la parte superior de la pila. De la misma manera las variables locales del método, se colocan dentro de un cartón y luego se aplia sobre las cajas de parámetros existentes. Cuando el método finaliza, se remueven las cajas de variables y parámetros respectivamente.

El mismo ciclo de vida de los elementos almacenados en la pila ocurre cuando se declaran variables dentro de paréntesis, como por ejemplo en una sentencia While, For, etc.

Ejemplo 1

En el siguiente código del programa TheStackTest1 tipo consola, creamos tres variables:

namespace TheStackTest1
{
    class Program
    {
        static void Main(string[] args)
        {
            int var1 = 20000;
            short var2 = 15;
            bool var3 = true;
        }
    }
}

Mediante las ventanas Call Stack y Locals de Visual Studio podemos ver la pila:

Como nos podemos dar cuenta, el primer elemento de la pila es el parámetro args del método Main, y el ultimo o el que se encuentra en la parte superior de la pila es la variable var3.

Para visualizar las ventana Call Stack y Locals, debemos ir al menú Debug, submenú Windows y elegir la opción Call Stack o Locals.

Ejemplo 2

En el siguiente código del programa TheStackTest2 tipo consola, creamos las mismas tres variables del Ejemplo 1, seguido de dos métodos más: Metodo1 y Metodo2.

using System;

namespace TheStackTest2
{
    class Program
    {
        static void Main(string[] args)
        {
            int var1 = 20000;
            short var2 = 15;
            bool var3 = true;

            Metodo1(var1, var2, var3);
        }

        static void Metodo1(int param1, short param2, bool param3)
        {
            decimal var4 = 5.05m;

            Metodo2(var4);
        }

        static void Metodo2(decimal param4)
        {
            string var5 = "FIN";

            Console.WriteLine(var5);
        }
    }
}

A continuación, se muestran las pilas de cada método:

Pila método 1.

Pila método 2.

Tal como se puede visualizar, al momento de llamar a un método, se añaden elementos a la pila (parámetros y variables) y una vez finalizados, se eliminan.

En estos ejemplos hemos usado tipos de datos predefinidos por C#, pero y ¿Si quisiéramos definir nuestras propias clases u objetos? Es aquí donde entra en juego el Montículo.

The Heap

El Montículo o Montón es un área de memoria donde se asignan fragmentos o espacios para almacenar ciertos tipos de objetos. A diferencia de la Pila, los datos pueden ser agregados y removidos en cualquier orden.

Las características generales del Montículo son las siguientes:

  1. Todo objeto es almacenado en el Montón, mientras la referencia al objeto está en la Pila.
  2. El tamaño del Montículo no es ilimitado, si al momento de crear un objeto no hay suficiente espacio, ocurrirá una excepción de tipo OutOfMemoryException, y el objeto no será creado.

Si tomamos nuevamente como referencia las cajas de cartón, en el caso del Montículo dichas cajas están esparcidas en el piso de una habitación sin un orden especifico.

Cuando un objeto es creado, el runtime busca una caja vacía del Montón de la habitación y lo coloca adentro. La referencia al objeto es almacenada en una variable local en la Pila. El runtime mantiene una pista del número de referencias a la caja donde se encuentra el objeto, ya que dos o más variables pueden hacer referencia a un mismo objeto. Cuando la última referencia al objeto desaparece, el runtime marca la caja como vacía o en desuso, y en este punto estará lista para que ese espacio de memoria quede disponible y se pueda almacenar otro objeto.

Los objetos almacenados en el Montículo no pueden ser eliminados de forma explícita. Por defecto, el Garbage Collector automáticamente limpia los objetos huérfanos cuando detecta que el código tiene un largo periodo de tiempo sin usarlos, con la ventaja que existan menos errores de memoria.

Observación

Cabe recalcar que el sistema se encarga automáticamente de la gestión de la Pila y el Montículo. Sin embargo, como desarrolladores es importante tener los conceptos claros de cómo funcionan estos mecanismos para la administración de datos en memoria.

Referencias

Ver la pila de llamadas y usar la ventana pila de llamadas del depurador

 

Operador Condicional NULL ?. en C#

0

En el desarrollo de software, una buena práctica antes de invocar a los miembros de un objeto es comprobar si el mismo fue instanciado correctamente. Para esto, comprobamos si su valor es diferente de null, si es verdadero, podemos usar sus miembros. De esta forma, evitamos que el compilador genere una excepción de tipo NullReferenceException, que detendría la ejecución del programa.

El Operador Condicional ?. es una forma abreviada de comprobar si el valor de un objeto es null, antes de tener acceso a sus miembros y hacer uso de ellos. La sintaxis es la siguiente:

Objeto?.Miembro

Que es equivalente a:

if (Objeto != null)
   Objeto.Miembro

En el siguiente ejemplo tenemos una clase llamada Persona, con una sola propiedad: Nombre. El método MostrarNombre muestra en pantalla el nombre de la persona.

static void MostrarNombre(Persona persona)
{
    if (persona != null)
       Console.WriteLine(persona.Nombre);
}

Con el uso del operador condicional null el resultado es el mismo:

static void MostrarNombre(Persona persona)
{
    Console.WriteLine(persona?.Nombre);
}

Podemos también usar múltiples operadores condicionales de forma combinada. El siguiente código es valido:

static void MostrarCiudadDireccion(Persona persona)
{
    Console.WriteLine(persona?.Direccion?.Ciudad);
}

En el caso de que persona sea null, el operador ?. no permite el acceso a Direccion ni Ciudad.

Referencias

?. y ?[]: operadores condicionales NULL (C# y Visual Basic)

Operador Condicional ?: en C#

0

El operador condicional ?: es una forma abreviada de escribir una sentencia if-else simple. La sintaxis es la siguiente:

Condicion ? Expresion1 : Expresion2;

Que equivaldría a:

if (Condicion)
   Expresion1;
else
   Expresion2;

Para implementarlo, debemos de tener en cuenta lo siguiente:

  1. La Condicion siempre debe de retornar un valor de tipo boolean.
  2. Si la Condicion es true, Expresion1 se evalúa y se convierte en el resultado. Caso contrario Expresion2 se evalúa y se convierte en el resultado.
  3. El tipo de dato de Expresion1 debe ser el mismo que el de Expresion2, caso contrario se debe realizar primero una conversión implícita de un tipo al otro.
  4. Los operadores condicionales anidados siempre son evaluados desde la derecha. La expresión Condicion1 ? Expresion1 : Condicion2 ? Expresion2_1 : Expresion2_2 es equivalente a Condicion1 ? Expresion1 : (Condicion2 ? Expresion2_1 : Expresion2_2).
  5. El operador condicional debe ser usado en sentencias de asignamiento, llamadas, incrementos, decrementos y expresiones de nuevos objetos que pueden ser usados como una sentencia.

Ejemplo

bool var = true;

string resultado = var == true ? "Verdadero" : "Falso";

Console.WriteLine(resultado);

Resultado:

Verdadero

Referencias

Operador ?: (Referencia de C#)

Declaración e Inicialización de Múltiples Variables en C#

0

En C# se pueden declarar varias variables en una sola sentencia. Sin embargo se deben seguir las siguientes pautas:

  1. Todas las variables deben ser del mismo tipo.
  2. Cada variable debe estar separada por una coma.
  3. NO se pueden inicializar dos o mas variables con un mismo valor.

Ejemplo

//Cada variable tiene su propio valor inicial.
int var1 = 10, var2 = 10, var3 = 20;

//var4 NO tiene inicializador, por lo tanto el compilador le asigna el valor default para tipos Boolean, False.
bool var4, var5 = true;

//var7 NO tiene inicializador, por lo tanto el compilador le asigna el valor default para tipos double, 0.
double var6 = 5.50, var7, var8 = 1.98;

//ERROR! variables de diferentes tipos.
int var8, float var9; 

 

Tipos de Datos Personalizados en C#

0

Además de los 16 tipos predefinidos proporcionados por C#, también se puede crear tipos propios, personalizados o definidos por el usuario.

Hay seis tipos que se pueden crear:

  1. class.
  2. struct.
  3. array.
  4. enum.
  5. delegate.
  6. interface.

Tipos de Datos Integrados en C#

0

C# proporciona 16 tipos de datos integrados o predefinidos para representar números enteros, decimales, de punto flotante, valores booleanos, caracteres, cadenas de caracteres, y otros tipos. De estos, 13 se denominan simples.

Los 13 tipos predefinidos simples incluyen:

    • Once tipos numéricos:
      • Ocho tipos enteros de varias longitudes, con y sin signo: sbyte, byte, short, ushort, int, uint, long y ulong.
      • Dos tipos de punto flotante: float y double.
      • Un tipo de mayor precisión llamado decimal, que a diferencia de float y double, puede representar números con fracciones exactas. Lo que lo hace adecuado para cálculos financieros, monetarios, operaciones aritméticas, etc.
    • Un tipo de caracter unicode, llamado char.
    • bool, Un tipo que representa dos valores, verdadero y falso.

Los 3 tipos restantes o no simples son:

  • object, que es el tipo base de todos los demás tipos.
  • string, el cual representa un arreglo de caracteres Unicode.
  • dynamic, el cual es usado para escribir assemblies en lenguajes dinámicos.

Tabla de Tipos Integrados Simples

Todos los tipos integrados simples del lenguaje C#, son alias de tipos .NET Framework predefinidos en el espacio de nombres System. Por ejemplo, int es un alias de System.Int32.

La siguiente tabla muestra todos los tipos de datos integrados simples.

Tipo C# Intervalo Tamaño / Precisión Tipo .NET Default
sbyte De -128 a 127 Entero de 8 bits con signo System.SByte 0
byte De 0 a 255 Entero de 8 bits sin signo System.Byte 0
short De -32 768 a 32 767 Entero de 16 bits con signo System.Int16 0
ushort De 0 a 65.535 Entero de 16 bits sin signo System.UInt16 0
int De -2.147.483.648 a 2.147.483.647 Entero de 32 bits con signo System.Int32 0
uint De 0 a 4.294.967.295 Entero de 32 bits sin signo System.UInt32 0
long De -9.223.372.036.854.775.808 a 9.223.372.036.854.775.807 Entero de 64 bits con signo System.Int64 0
ulong De 0 a 18.446.744.073.709.551.615 Entero de 64 bits sin signo System.UInt64 0
float De ±1,5 x 10-45 a ±3,4 x 1038 7 dígitos System.Single 0.0f
double De ±5,0 × 10−324 a ±1,7 × 10308 15-16 dígitos System.Double 0.0d
decimal De ±1,0 x 10-28 to ±7,9228 x 1028 28-29 dígitos significativos System.Decimal 0m
char U+0000 a U+FFFF Carácter Unicode de 16 bits System.Char \x0000
bool Booleano true, false System.Boolean false

Tabla de Tipos Integrados No Simples

Los tipos de datos integrados no simples son:

Tipo C# Descripción Tipo .NET
object Es la clase base para todos los demás tipos, incluidos los tipos integrados simples. System.Object
string Una secuencia de caracteres Unicode. System.String
dynamic Es un tipo diseñado para ser usado con assemblies escritos en lenguajes dinámicos No corresponde a un tipo .NET

Comentarios

El alias y su tipo .NET son intercambiables. Por ejemplo, puede declarar una variable de tipo entero de dos formas:

int varInt1 = 1; // Usando un alias
System.Int32 varInt2 = 2; // Usando un tipo .NET Framework

Referencias

Tabla de tipos integrados (Referencia de C#)

Data Binding en WPF

0

El Enlace de Datos o Data Binding, es una de las características más poderosas de WPF. Básicamente, permite establecer de una forma simple, la conexión entre las propiedades de un objeto Origen y un objeto Destino, con el objetivo de que sus valores permanezcan sincronizados.

Data Binding

De esta forma, el objeto destino será notificado cuando las propiedades del objeto origen cambien su valor, o viceversa. Gracias a este canal de comunicación, los cambios se ven reflejados automáticamente.

Esta funcionalidad, presenta las siguientes ventajas con respecto a los métodos tradicionales de programación:

  • Un número mayor de propiedades de elementos WPF implementan Data Binding.
  • Menos código de programación.
  • Flexibilidad al momento de interactuar con el usuario (validación, conversión, formateo, etc.)
  • Separación bien definida de la lógica del negocio de la interfaz de usuario, lo que permite implementar patrones como MVVM.

Componentes del Enlace de Datos

La clase que permite el enlace de datos es la System.Windows.Data.Binding, y por lo general tiene mínimo cuatro partes:

  1. Un objeto origen: Que hace referencia al objeto de donde se obtienen los datos. Por lo general es una clase o entidad de la lógica del negocio, sin embargo, puede también ser una lista, un objeto ADO.NET, un control, o cualquier objeto CLR. El nombre del objeto origen se lo especifica en el objeto destino mediante la propiedad ElemenName, de la clase Binding.
  2. Un objeto destino: Que debe ser del tipo DependencyObject, y por lo general es un control WPF. Puesto que todos los controles heredan de la clase UIElement y esta a su vez de DependencyObject, los mismos aplican perfectamente como destino del enlace.
  3. Una propiedad destino: Que debe ser una Propiedad de Dependencia o DependencyProperty. La mayoría de las propiedades UIElement son DependencyProperty, y solo los objetos DependencyObject pueden definir propiedades de dependencia.
  4. Una ruta: Que representa el valor o la propiedad del objeto origen que se va a enlazar. Se la especifica en el objeto destino mediante la propiedad Path de la clase Binding.

Ejemplo

En el siguiente ejemplo tenemos un control TextBlock (Objeto Destino) enlazado a un control TextBox (Objeto Origen), cuya propiedad a sincronizar es la Text.

<StackPanel Margin="6">
   <TextBox Name="ObjetoOrigen" />
   <TextBlock Name="ObjetoDestino"
              Text="{Binding ElementName=ObjetoOrigen, Path=Text}" />
</StackPanel>

El resultado de este ejemplo básico es que todo texto que se ingrese en el TextBox (Objeto origen), se verá reflejado en el TextBlock (Objeto destino).

 

 

 

 

 

Direcciones en WCF

0

La dirección o Address, es la primera parte del ABC de WCF. La misma define donde se encuentra ubicado el servicio, puede ser un servidor remoto en internet, en la intranet, o una misma PC.

La dirección contiene toda la información necesaria para que los clientes puedan conectarse a él, o los endpoints del servicio. Está compuesta de las siguientes partes:

[transporte]://[maquina][:puerto]/[ruta]

Transporte

El protocolo de transporte (Transport Protocol o Transport Scheme) es el medio de comunicación de él, o los endpoints del servicio. WCF soporta los siguientes Esquemas de Transporte:

  1. HTTP/HTTPS.
  2. TCP.
  3. IPC.
  4. MSMQ.
  5. Service Bus.
  6. WebSocket.
  7. UDP.

Direcciones HTTP

Las direcciones HTTP usan como transporte el protocolo http o http seguro (https). Es el transporte preferido para la web.

El formato de las direcciones HTTP es:

http://[nombre de dominio o nombre de servidor o dirección IP][:puerto]/[ruta]

Algunas ejemplos de direcciones HTTP:

http://midominio:8001/MiServicio

http://localhost:8001

http://172.217.4.4:8001/publico/MiServicio

Si no se especifica el puerto, el default es el 80 y 443 para HTTPS.

https://midominio.com/MiServicio

Direcciones TCP

Como su nombre lo indica, las direcciones TCP usan como transporte el protocolo TCP. Por lo general incluyen el número de puerto al cual conectarse.

net.tcp://localhost:8001/MiServicio

Cuando el puerto no es especificado, el default es el 808.

net.tcp://localhost/MiServicio

Es posible que dos direcciones TCP del mismo host, compartan un mismo puerto:

net.tcp://localhost:8001/MiServicio
net.tcp://localhost:8001/MiOtroServicio

Direcciones IPC

Las direcciones Inter Process Communication (IPC) usan net.pipe como transporte. Estos servicios solo pueden comunicarse con los clientes de la misma máquina. Tampoco es permitido que dos direcciones compartan el mismo nombre.

net.pipe://localhost/MyPipe

Direcciones MSMQ

Las direcciones Microsoft Message Queue (MSMQ) usan como transporte net.msmq. Se debe especificar el nombre de la cola y a su vez su tipo. Si se omite, por default la cola es pública.

net.msmq://localhost/privado/MiServicio
net.msmq://localhost/MiServicio

Direcciones WebSocket

Las direcciones WebSocket son usadas cuando se requiere callback en internet. Si no se especifica el puerto, se usará el default de HTTP.

ws://localhost:8001

Direcciones UDP

Las direcciones UDP usan soap.udp como transporte.

soap.udp://localhost:8081