Archivo de la etiqueta: .Net Framework

PAGING DATA, NOT CLIENT SIDE (2) (Blazor client)


In the previous post,Paging data, NOT client side. | Universidad Net, I described how to use OFFSET-FETCH pair to paginate data in the server side. In this one, I will describe an example of how to use it in a Web Assembly App.

Note: To test this code, you must create a new Blazor app in Visual Studio.

The Model.

The data model will be a class with two properties, one for the list of items and other with the page’s information.

This classes will be added in the Shared project of the Application.

The first class vary depending on the data you want to display, but the second one will be always the same, enhanced by some code.

Since the pager class will be a standardized and enhanced version of the data retrieved by using the stored procedure, and could be used in different projects, let’s define an interface for it.

IPagesInfo interface.

This is the code for the Interface.

public interface IPagesInfo
{
   #region Properties
 
   /// <summary>
   /// Gets or sets the current page.....
   /// </summary>
   System.Int32 CurrentPage { getset; }
 
   /// <summary>
   /// Gets the current first page number..
   /// </summary>
   Int32 FirstPageNumber { get; }
 
   /// <summary>
   /// Gets the HasNextGroup
   /// Gets a value indicating whether this instance has next page..
   /// </summary>
   Boolean HasNextGroup { get; }
 
   /// <summary>
   /// Gets the HasPreviousGroup
   /// Gets a value indicating whether this instance has previous page..
   /// </summary>
   Boolean HasPreviousGroup { get; }
 
   /// <summary>
   /// Gets the last page number..
   /// </summary>
   Int32 LastPageNumber { get; }
 
   /// <summary>
   /// Gets or sets the number of links to show..
   /// </summary>
   Int32 NumberOfLinks { getset; }
 
   /// <summary>
   /// Gets or sets the size of the page.....
   /// </summary>
   System.Int32 PageSize { getset; }
 
   /// <summary>
   /// Gets or sets the Qty of rows skipped from the top of the select.....
   /// </summary>
   System.Int32 Skip { getset; }
 
   /// <summary>
   /// Gets or sets the Qty of rows taken.....
   /// </summary>
   System.Int32 Take { getset; }
 
   /// <summary>
   /// Gets or sets the amount of items to display.
   /// </summary>
   System.Int32 TotalItems { getset; }
 
   /// <summary>
   /// Gets the total pages available to display..
   /// </summary>
   Int32 TotalPages { get; }
   /// <summary>
   /// Gets the page number for the previous group start.
   /// </summary>
   /// <value>
   /// The page number.
   /// </value>
   Int32 PreviousGroupStart { get; }
   /// <summary>
   /// Gets the  page number for the next group start.
   /// </summary>
   /// <value>
   /// The page number.
   /// </value>
   Int32 NextGroupStart { get; }
   #endregion
}

PagerInfo class.

Here, you have the code for the class implementing the IPagesInfo interface.

Notice the class is responsible of the calculations about page numbers displayed, if there are next or previous groups of pages, etc.

public class PagerInfo : IPagesInfo
{
   #region Fields
 
   /// <summary>
   /// Defines the lastPageNumber.
   /// </summary>
   internal Int32 lastPageNumber = 0;
 
   #endregion
 
   #region Properties
   public Int32 TotalPages
   {
      get => (Int32)Math.Ceiling(TotalItems / (Double)PageSize);
   }
   /// <summary>
   /// Gets the page number for the previous group start.
   /// </summary>
   /// <value>
   /// The page number.
   /// </value>
   public Int32 PreviousGroupStart => LastPageNumber - NumberOfLinks;
   /// <summary>
   /// Gets the  page number for the next group start.
   /// </summary>
   /// <value>
   /// The page number.
   /// </value>
   public Int32 NextGroupStart => LastPageNumber + 1;
 
   /// <summary>
   /// Gets or sets the current page.
   /// </summary>
   public System.Int32 CurrentPage { getset; }
 
   /// <summary>
   /// Gets the current first page number..
   /// </summary>
   public Int32 FirstPageNumber
   {
      get => LastPageNumber - (NumberOfLinks - 1);
   }
 
   /// <summary>
   /// Gets a value indicating whether this instance has next page.
   /// </summary>
   public Boolean HasNextGroup
   {
      get => CurrentPage + NumberOfLinks < TotalPages;
   }
 
   /// <summary>
   /// Gets a value indicating whether this instance has previous page.
   /// </summary>
   public Boolean HasPreviousGroup
   {
      get => CurrentPage > NumberOfLinks;
   }
 
   /// <summary>
   /// Gets the last page number..
   /// </summary>
   public Int32 LastPageNumber
   {
      get
      {
         lastPageNumber = (Int32)Math.Ceiling((Double)CurrentPage / NumberOfLinks) * NumberOfLinks;
         if (lastPageNumber > TotalPages)
         {
            lastPageNumber = TotalPages;
         }
         return lastPageNumber;
      }
   }
 
   /// <summary>
   /// Gets or sets the number of links to show..
   /// </summary>
   public Int32 NumberOfLinks { getset; } = 10;
 
   /// <summary>
   /// Gets or sets the size of the page.....
   /// </summary>
   public System.Int32 PageSize { getset; }
 
   /// <summary>
   /// Gets or sets the Qty of rows skipped from the top of the select.....
   /// </summary>
   public System.Int32 Skip { getset; }
 
   /// <summary>
   /// Gets or sets the Qty of rows taken.....
   /// </summary>
   public System.Int32 Take { getset; }
 
   /// <summary>
   /// Gets or sets the amount of items to display.
   /// </summary>
   public System.Int32 TotalItems { getset; }
 
   /// <summary>
   /// Gets the total pages available to display..
   /// </summary>
 
   #endregion
 
}

The Data Class.

This class will contain your data and the IPagesInfo properties. In this sample, it will be called PersonsPager.

Note: You can easily create it by executing the stored procedure, copying the result and in a new code window (for example, an empty one created for PersonsPager), paste it by the Edit – Paste Special-Past JSON as Classes.

Then replace the names autogenerated by your own and change the type for the second one for the PagesInfo class.

The pasted code names the main class as Rootobject, which has been renamed to PersonsPager.

The Pager class will be changed by PagesInfo, and the Pager class defined could be removed.

This is the final code for the PersonsPager class.

public class PersonsPager
{
 
   public List[] List { getset; }
   public PagerInfo Pager { getset; }
}
public class List
{
   public int BusinessEntityID { getset; }
   public string FirstName { getset; }
   public string MiddleName { getset; }
   public string LastName { getset; }
}

The Pager Component.

You must add a Razor Component. In this sample, it is called Pager.razor.

In the UI code, I use an UL tag, adding a button for previous group of pages (when the user moves beyond the first set of page numbers), buttons for the different page numbers, and a button for the next group as well.

For the buttons, the pagination, page-item, and page-link classes are used.

<nav class="text-center">
   <ul class="pagination">
      @{//Previous page group button.
 
         string className = "page-item " + (!PagesInfo.HasPreviousGroup ? " disabled" : ""); //If there is in previous group, the button will be disabled
         <li class="@className">
            <button class="page-link "
                    @onclick="@(()=>
                                   {
                                      if (PagesInfo.HasPreviousGroup)
                                        ChangePage(PagesInfo.PreviousGroupStart);
                                   }
               )">
               ⏪
            </button>
         </li>
      }
      @*Buttons for page numbers*@
      @for (Int32 i = PagesInfo.FirstPageNumber; i <= PagesInfo.LastPageNumber; i++)
      {
         int pageSelector = i;
 
         className = "page-item " + (i == PagesInfo.CurrentPage ? " active" : "");
         <li class="@className">
            <button class="page-link" @onclick="@(()=>ChangePage(pageSelector))">@string.Format("{0:00}"pageSelector)</button>
         </li>
      }
      @{//Next page group button.
         className = "page-item  " + (!PagesInfo.HasNextGroup ? " disabled" : "");
         <li class="@className">
            <button class="page-link " @onclick="@(()=>
                                                      { if (PagesInfo.HasNextGroup)
                                                            ChangePage(PagesInfo.NextGroupStart);
                                                      }
               )">
               ⏩
            </button>
 
         </li>
      }
   </ul>
</nav>

In the code section, parameters are defined for:

  • an instance of a class implementing the IPagerInfo
  • a value to persist the selected page
  • an EventCallback to notify the client page about the changes in the selection by the user.

Finally, the ChangePage function called every time the user clicks ant of the buttons is defined to change the selected page and notify the client page.

@code {
   [Parameter]
   public IPagesInfo PagesInfo { getset; }
   [Parameter]
   public int SelectedPage { get => PagesInfo.CurrentPage; set => PagesInfo.CurrentPage = value; }
   [Parameter]
   public EventCallback OnPageChange { getset; }
   void ChangePage(int newPage)
   {
      PagesInfo.CurrentPage = newPage;
      SelectedPage = newPage;
      OnPageChange.InvokeAsync(SelectedPage);
   }
}

The controller.

To get the information from the database, you will need an API REST controller which returns the JSON string from the stored procedure.

The method must be decorated with the HttpGet attribute to react when the client page code calls it.

[Route("/[controller]")]
[ApiController]
public class PersonDataController : ControllerBase
{
   public PersonDataController(IConfiguration configuration)
   {
      Configuration = configuration;
   }
 
   public IConfiguration Configuration { get; }
 
   [HttpGet]
   public async Task<stringGetPersons(int selectedPageint pageSize = 10)
   {
      using SqlConnection con = new SqlConnection(Configuration.GetConnectionString("aw"));
      try
      {
         SqlCommand com = new SqlCommand("[Person].[Person_GetforPager]"con);
         com.CommandType = System.Data.CommandType.StoredProcedure;
         com.Parameters.AddWithValue("@skip", (selectedPage == 0 ? 0 : selectedPage - 1* pageSize);
         com.Parameters.AddWithValue("@take"pageSize);
         con.Open();
         string values = (await com.ExecuteScalarAsync()).ToString();
         return values;
      }
      catch (System.Exception ex)
      {
 
         throw;
      }
   }
}

The client page.

A new component will be defined to display the data.

It will contain any type of display for your data (in the sample, it is just a UL list), and an instance of the Pager component.

As usual in a Web assembly app, you must check if you have data to display before performing the UI generation.

Moreover, you can decide if you need display the pager component in case of no more than one page is needed to display the information.

@inject HttpClient httpClient
<h3>Persons</h3>
@if (result != null)
{
   <div>@result.StatusCode</div>
   <div>@result.Content.ReadAsStringAsync().Result</div>
   <div>@httpClient.BaseAddress</div>
}
@if (personsPager != null && personsPager.List.Count() > 0)
{
   <ul>
      @foreach (var item in personsPager.List)
      {
         <li>@item.LastName</li>
 
      }
   </ul>
   @if (personsPager.Pager != null && personsPager.Pager.TotalPages > 1)
   {
      <Pager PagesInfo="PagesInfo" OnPageChange="ChangePage" />
      @**@
   }
}
else SelectedPage = 1;

The code of this page will call the controller get method to retrieve the information.

@code {
   PersonsPager personsPager;
   int SelectedPage;
   public IPagesInfo PagesInfo { get => personsPager.Pager; }
   HttpResponseMessage result;
   protected async override Task OnInitializedAsync()
   {
      await GetDataAsync();
   }
   async void ChangePage()
   {
      SelectedPage = personsPager.Pager.CurrentPage;
      //SelectedPage = PagesInfo.CurrentPage;
      await GetDataAsync();
      this.StateHasChanged();
 
   }
   async Task GetDataAsync()
   {
      //result = await httpClient.GetAsync($"PersonData?Selectedpage={SelectedPage}");
      personsPager = await httpClient.GetFromJsonAsync<PersonsPager>($"PersonData?Selectedpage={SelectedPage}");
 
   }
 
}

And this is the result:

Persons page with Pager sample
Persons page with Pager sample

Note: The “aw” connection string points to the AdventureWorks2017 database where the stored procedure from the previous post has been created.

BTW here is the sample

Encapsular en tus clases


Cualquier conjunto de instrucciones que crees, deben estar necesariamente dentro de un miembro (member). Estos conjuntos de código deben estar contenidos en algún archivo que representa una unidad de código.

En Visual Basic, existen los módulos, que pueden cumplir esta función. (En realidad, esto se hereda de las primeras implementaciones del lenguaje, donde los miembros que se utilizaban como genéricos, podían estar en módulos globales).

Por herencia, esa funcionalidad sigue existiendo para el .Net Framework, pero C# no dispone de esa forma de contenedor. Para C# solo existen clases.

Esto no es ni malo, ni bueno.

Si se convierte un módulo de VB, a C#, basta con definir una clase como estática… y lo demás será igual (bueno, los miembros también deben declararse como estáticos).

Por otra parte, en el propio Visual Basic, también es posible declarar métodos estáticos.. pero no tiene la funcionalidad de clase estática.. para ello, ya tiene el módulo.

Considera que, una clase estática, podría ser como una biblioteca (y aclaro, es biblioteca NO “librería” Sonrisa ), de tus funciones personalizadas “de uso directo”. Por ejemplo, si haces muchos programas financieros, podrías tener métodos estáticos para cálculos de interés, de amortización etc.

Sin embargo, en muchas otras oportunidades, una clase podrá representar una entidad de tu problemática y en dicho caso, podrás considerarlas como plantillas para crear distintas ocurrencias de dichas entidades.

Pero, en definitiva, una clase es una plantilla (template), que contiene código relacionado. 

En ella, los miembros podrán ser de distinto tipo:

Propiedades

Representan los “atributos” de cada entidad (instancia). Por ejemplo, si quiero representar un color, podría definir una clase que exponga las propiedades Rojo, verde y azul.

Definirlas como propiedades, permite que se puede ejecutar código dentro de ellas. Así, cuando se le asigna un valor, establecer un cálculo, o validar que el valor esté dentro de un determinado rango.

O asignar un valor predeterminado cuando  otro código reclama el valor.

Estamos entonces definiendo que, en realidad una propiedad es una combinatoria de dos miembros: el de asignar el valor, y el de obtener el valor.

VB

        Private m_Rojo As Integer
        Public Property Rojo() As Integer
            Get
                Return m_Rojo
            End Get
            Set(ByVal value As Integer)
                m_Rojo = value
            End Set
        End Property

CS

        int m_Rojo = 0;
        public int Rojo
        {
            get
            {
                return m_Rojo;
            }
            set
            {
                m_Rojo = value;
            }
        }

 

Si embargo, se puede obviar una de las dos partes.

Dejar la propiedad como de solo lectura.

VB

        Private m_Rojo As Integer
        Public ReadOnly Property Rojo() As Integer
            Get
                Return m_Rojo
            End Get
        End Property

CS

        int m_Rojo = 0;
        public int Rojo
        {
            get
            {
                return m_Rojo;
            }
        }

 

Dejar la propiedad como de solo  escritura

VB

        Private m_Rojo As Integer
        Public WriteOnly Property Rojo() As Integer

            Set(ByVal value As Integer)
                m_Rojo = value
            End Set
        End Property

CS

        int m_Rojo = 0;
        public int Rojo
        {
            set
            {
                m_Rojo = value;
            }
        }

 

O definir la propiedad sin asignar código específico.

VB

Public Property Rojo As Integer

CS

        public int Rojo { get; set; }

Campos.

Son formas especiales de propiedades, que se basan en simplemente definir la variable, su tipo y su “exposición” (que nivel de otros objetos pueden utilizarlo).

Por lo demás, son variables dentro de la clase.

        Public Verde As Integer

        public int Verde;

Métodos.

Son conjuntos de código para ejecutar.

Podemos sub clasificarlos entre los que no retornan ningún valor (procedimientos), y los que si retornan un valor (funciones).

En C#, en realidad, todos retornan un valor (o sea, son funciones). Pero en el caso de no necesitar que retorne nada, se declara el tipo que se retorna como void.

 

        Sub HacerAlgo()

        End Sub
        Function RetornarAlgo() As Integer

        End Function

        public void HacerAlgo()
        {
        }
        public int RetornarAlgo()
        {
        }

Así mismo, un método admite que se le pasen argumentos:

        Sub HacerAlgo(argumento As String)

        End Sub

        public void HacerAlgo(string argumento)
        {
        }

Los argumentos pueden ser opcionales, en cuyo caso, se requiere que se le asigne un valor predeterminado. además, una vez que se declara un argumento como opcional, todos los que siguen deben serlo.

        Sub HacerAlgo(Optional argumento As String = "Valor")

        End Sub

        public void HacerAlgo(string argumento="Valor")
        {
        }

O declarar un conjunto de argumentos de los que no se conoce la cantidad. En este caso, se declara como vector, arreglo (Array), para que se asignen todos los que hagan falta.

        Sub HacerAlgo(ParamArray argumentos() As String)

        End Sub

        public void HacerAlgo(params string[] argumentos)
        {
        }

Dejamos para más adelante, definir otras cosas, como los eventos.

Pero con estos elementos, ya podremos empezar a crear cosas útiles.

Incluido en el envase… (del .Net Framework)


Realizar programas es como cocinar.  (Idea que he “robado” al amigo Miguel Egea, @miguelEgea https://twitter.com/miguelEgea). Combinar distintos ingredientes para obtener un resultado novedoso.

Comenzando con las sentencias, tipos de datos, variables que hemos visto previamente, hasta componentes pre armados “listos para usar”

Sin embargo, mucho de ese código debe realizar tareas complejas, de mucho cuidado, como manipular posiciones de memoria, acceder a recursos de hardware o sencillamente, realizar operaciones complicadas.

Esos conjuntos de códigos vienen pre armados y encapsulados en el .Net Framework,

El .Net Framework.

Podríamos describir el .Net Framework como constituido por 3 partes:

  • El motor de ejecución común (CLR).
  • El Conjunto de clases base
  • Los lenguajes de programación compatibles (en realidad, no solo los lenguajes, sino también sus compiladores)

El Motor de Ejecución Común.

Diagrama del CLR

Como vemos en la imagen, hay una serie de componentes que integran el CLR de modo de implementar en forma encapsulada, las acciones de bajo nivel.

Para destacar que hay un componente responsable de “cargar” los tipos (clases) en memoria asegurándose de darles el espacio suficiente, asignarle un identificador único, etc.

O el compilador de MSIL (que merece una aclaración): Para permitir que el mismo código pueda ejecutarse en distintas configuraciones de computación, éste no se compila en código binario sino en un “lenguaje intermedio” que luego, cuando se comienza a ejecutar, se termina de compilar a código nativo.  Aunque parezca más laborioso y demorado, la realidad es que es imperceptible para el usuario. Además, si realmente es preocupante este hecho (que no se justifica realmente), existe, entre las herramientas del .Net Framework un “compilador a código nativo” que se puede ejecutar en la máquina destino, para dejarlo ya listo. (NGEN.exe).

Otra parte interesante es el GC (Garbage collector, o recolector de residuos). Una de las problemáticas  en los anteriores mecanismos de programación de C++ a Visual Basic, era la posibilidad de “descuidar” las instancias de los objetos creados, que dejaban de ser referenciados por el código del programa… pero nunca se descargaba de memoria. El GC se encarga de ver revisar cada instancia y ver si “hay algún código en ejecución que la conozca”. Si no, se encarga de eliminarla. (En realidad, si la clase utilizara recursos de bajo nivel, primero se encarga de disponer dichos recursos para que no sigan en uso, y luego, descargar la instancia de la memoria). Otra consecuencia de esto, que se debe tener en cuenta, es que si un proceso crea una instancia de una clase, es factible que se le entregue una que está “en espera” en el GC para ser destruida, que es más rápido. Por ello se recomienda que no se asuma que las variables tengan valores predeterminados.  Es importante inicializar las variables en cada creación.

La biblioteca de Clases Base.

Biblioteca de Clases Base

Son todas aquellas clases que nos permiten interactuar directa o indirectamente con los recursos electrónicos de la máquina, memoria, procesador, periféricos, disco, y otros más complejos, pero, en todos los casos, el fundamento sobre el que se basa toda la ejecución.

Por ejemplo, los tipos de datos están todos definidos allí.

Vemos que además de las BCL, existen otros conjuntos ya pre armados, de funcionalidades específicas como manejo de datos, manipulación de XML, y especializaciones para funcionalidades de alto nivel, como formularios de ventanas de Windows, aplicaciones Web, etc.

La siguiente es una lista de los conjuntos de funcionalidades más importantes incluidas en la BCL.

  • Administración de componentes Web que corren tanto en el servidor como en el cliente (ASP.NET)
  • Administración de memoria
  • Aleatoriedad
  • Auto descripción de código
  • Cifrado de datos
  • Compilación de código
  • Generación de código
  • Herramientas de despliegue de gráficos (GDI+)
  • Herramientas de seguridad e integración con la seguridad del sistema operativo
  • Interacción con el API Win32 o Windows API.
  • Interacción con los dispositivos periféricos
  • Interacción con otras aplicaciones
  • Manejo de arreglos de datos y colecciones
  • Manejo de cadenas de caracteres y expresiones regulares
  • Manejo de datos (ADO.NET)
  • Manejo de idiomas
  • Manejo de tipos de datos unificado
  • Manejo del sistema de ventanas
  • Manejo y administración de excepciones
  • Manipulación de archivos de imágenes
  • Manipulación de fechas, zonas horarias y periodos de tiempo
  • Operaciones aritméticas
  • Transmisión y recepción de datos por distintos medios (XML, TCP/IP)

Pero.. ¿Que es una clase?.

En definitiva es la plantilla que incluye variables, propiedades, métodos y eventos relacionados con una funcionalidad específica. 

Cuando necesitas usar una, creas una instancia y luego comienzas a utilizarla.

Existe además una forma especial de clases de instanciación automática, que sirven para realizar tareas. En ellas, cuando se utiliza uno de sus miembros (propiedad o método), se instancia sola y sirve para cualquier otro uso durante la ejecución del programa completo. Se denominan clases estáticas.

Espacios de Nombres. (Namespaces)

Teniendo tantas variedades y posibilidades, se podría transformar en una maraña incontrolable el conjunto de código.

Para mantener el orden, se agrupan en espacios de nombres, que además pueden ser jerárquicos.

Así, por ejemplo, las clases que permiten conectarse con un servidor de datos SQL Server están en:

System.Data.SqlClient

Esto permite además agrupar conjuntos de clases por “autor” (veremos más adelante que los permisos de ejecución de un componente pueden administrarse basados en espacios de nombres y/o ensamblados, que veremos ahora mismo.

Así, cosas que Microsoft ha hecho específicos, están dentro de un espacio de nombres que comienza precisamente por Microsoft como por ejemplo:

Microsoft.Win32.SafeHandles

… y también podremos crear los nuestros.

Ensamblados.

Ensamblado NET

Uno o más espacios de nombres, se compilan en bibliotecas de carga dinámica (archivo de extensión DLL),  que ante tantas combinatorias posibles, necesitan ser más identificables, requieren descriptores de sus contenidos, pueden utilizar textos, imágenes y otros recursos que se utilizan por las clases. El conjunto de todos esos elementos se denomina Ensamblado.

Las descripciones se contienen en un manifiesto, que describen unívocamente el ensamblado completo, y los metadatos de tipo, que contienen la descripción de cada una de las clases (recordemos, una clase es la plantilla de in Tipo), contenidas en el ensamblado.

El ensamblado completo queda entonces contenido dentro del archivo dll.

Un detalle: Nada condiciona que un espacio de nombres este contenido en un ensamblado. así, tipos distintos de un mismo espacio de nombres, pueden estar contenidos en distintos ensamblados. Eso asegura extensibilidad en la funcionalidad. Si yo necesito, digamos, un par de clases del espacio de nombres “espacio1” debo definir una referencia al ensamblado (y su correspondiente dll) que la contiene.

Luego, si necesito otra clase, puede ser que “No la encuentre” dado que su funcionalidad, más específica, se haya incluido en otro ensamblado (aunque pertenezca al mismo espacio de nombres). Deberé entonces agregar una referencia al otro ensamblado.

 

En la próxima entrega, empezaremos ya a recorrer las BCL más importantes y generar código de herramientas útiles en general.

Los datos (por referencia)


Como decíamos en la publicación pasada, los datos simples, se pueden almacenar en tipos por valor. Pero si la cosa se complica un poco, entonces resulta imposible reservar el espacio o equipararlo a registros del procesador.

Tomemos, por ejemplo, las cadenas de caracteres: Si estuviésemos hablando de un caracter (el tipo char), ocupa lo mismo que un byte (en realidad, que 2, dados los caracteres especiales de idiomas no latinos). Cada letra, número o símbolo, se representa por una combinatoria de bits hay estipulada en tablas predefinidas. (Caracteres ANSI, ASCII, Windows.. hay varios ).

PERO, si queremos almacenar palabras (imprescindible), el tema se complica.

Antiguamente, se utilizaban tipos de datos declarados como variables de longitud fija (y lo que sobra, a llenarlo con espacios).

Además de una perdida de espacio, resulta bastante poco eficiente para, por ejemplo, editores de texto, aún cuando diccionarios, listas de palabras etc. .., podrían aprovecharse de ese tamaño fijo para proceder con ordenamientos o búsquedas más rápidas. Pero por lo demás, resulta imposible de administrar en todos los usos posibles.

Así, se define teóricamente una cadena de caracteres como… bueno, casi eso. Un arreglo, vector, matriz o tabla (array) de caracteres. Cada una, de su propia cantidad de elementos (dependiendo de las letras a almacenar).

EL procesador no se “copia” el dato… usa un puntero hacia donde se encuentre dicha cadena en memoria y utiliza la misma… asunto resuelto…

Hasta que hay que agregar caracteres. La memoria se reserva para los caracteres que contiene la variable. Si agrego caracteres, “más allá” de lo previsto puedo reemplazar parte de la memoria de otras cosas.. (¿Les suena “se ha producido un error de protección general y el programa se apagará?.. eso es, cuando “el Windows se cuelga” es porque alguien no controló este tipo de cuestiones).

Incrementar, reemplazar partes, reducir cadenas de caracteres es una operación de 3 pasos: copio la original en otra, insertando donde sea, cambio el puntero a la nueva dirección de memoria, y descarto el espacio anterior (o le digo al sistema, sencillamente, que puede usarlo).

Vamos, se complica. Y si encima, empezamos a definir tipos de datos más complejos, con mayor razón se deben operar como referenciados. Es lo que sucede con todos los otros tipos de datos.

Y si lo vemos desde la perspectiva del .Net Framework, hay algunas otras definiciones a tener en cuenta.

Dado que en el .Net Framework se utiliza mucho la herencia, (de la cual hablaremos más adelante), todos los tipos de datos tienen relación entre sí. De momento, digamos en general que, todo dato (tipo desde ahora) que se pueda almacenar hereda de un tipo súper básico: objeto (object).

Esto es tan así, que hasta los tipos por valor, que hablamos anteriormente, en la definición del .Net Framework, heredan de object.

Es por eso que todos los tipos de datos poseen algunos métodos que son comunes.

clip_image001

Y vamos a definir cada uno de estos métodos, para ir entendiendo como se hacen las cosas en .Net.

Equals

Compara el objeto actual con otro para evaluar si son iguales. Sin embargo, depende del tipo de dato que estemos hablando.

En el caso de los tipos por referencia, la comparativa se realiza utilizando ReferenceEquals.

Si los tipos son por valor, lo que se comparan son los valores.

GetHashCode

Obtiene el valor identificativo único de una instancia, específicamente pensado para ser manipulada dentro de una tabla o lista hash (como podría ser un diccionario)

Esto implica que no se puede confiar en este valor como identificativo único en el tiempo, ni entre aplicaciones. En realdad, salvo para el caso puntual de las listas mencionadas, no debiera considerarse dicho valor como útil.

GetType

Existe un tipo de dato en el .Net Framework que permite identificar cualquier otro. Esto es, cada tipo de datos es de un Tipo, y por lo tanto, hay un Type que lo representa.

El método GetType, nos permite obtener el tipo que define esta instancia en particular.

A su vez, el Type obtenido, nos permite identificar los miembros (Propiedades, campos, métodos, eventos) que ese tipo implementa.

MemberwiseClone

Crea una copia “superficial” del objeto en otro.

En tipos por valor, copia el valor bit a bit.

En tipos por referencia, copia el valor de la referencia, bit a bit… o sea que ambos apuntan a la misma instancia.

Si lo que se quiere es hacer una copia independiente (también llamada profunda), es necesario transferir todos los valores desde el origen al destino.

Algunos tipos implementan un método específico (CopyTo), para esto.

ReferenceEquals

Establece si el objeto referencia a la misma posición de memoria del objeto pasado como argumento. Así, sabremos exactamente si son el mismo, o no, aun cuando los valores de todas sus propiedades sean idénticos.

ToString

Genera una representación en cadena de caracteres del contenido del objeto. En muchos casos, lo que se retorna es el valor de dicha variable, representado como cadena (como es el caso de los tipos por valor en general). En cambio, en otros, la mayoría de los tipos por referencia, se retorna el nombre del tipo.

Sin embargo, muchos exponen formas complejas como ser tipo y valor principal… Esto depende de cómo se haya diseñado el objeto.

Pero, en sí mismos, se asume que el método ToString siempre retorna un valor significativo.

Así entonces, toda tipo, (insisto inclusive los que son por valor), heredan, directa o indirectamente, de Object y, por tanto, comparten estos métodos. Y aquí empieza la “magia” de esta historia.

Tenemos mucho que ver respecto de las herencias.

Hasta la siguiente.

Tipos de datos por valor en el .Net Framework


Un programa no es ni más ni menos que una automatización que se aplica a un conjunto de información, de datos.

Algunas veces son los datos de un elemento en particular, otras de muchos datos similares. A veces son cosas que pasaron antes, y otras, los sucesos del mismo momento (un juego, por ejemplo).

Sin embargo, sigue siendo, siempre, manipulación de datos.

Los usamos en la vida diaria, los usamos en cada momento en un programa, o en una aplicación que usamos… y sin ton ni son, sin entender muy bien que son, como son, como los entiende un procesador… así que, a ello vamos. A definir los datos.

Empecemos por entender que hace el procesador con los datos.

Calculando. (lo primero que hicieron los procesadores)

Desde el principio de la historia (informática :)), ha sido igual. Empezar por hacer algo del estilo 2+2=4 .. Pero muchas veces y muy rápido.

Eso se hace “moviendo” datos a espacios particulares e la circuitería del procesador, llamados registros. Los procesadores modernos se basan en la arquitectura IA32 (que se extendió este siglo a la IA64) ya que por una parte es eficiente y por otra, mantienen compatibilidad.

Se podría hablar mucho de esto, pero la realidad es que nos importa entender, desde el punto de vista de los datos, algo muy básico. Los datos se cargan y operan en los registros de maneras muy simples.

  1. Se mueve un valor a un registro
  2. Se mueve el otro valor al segundo registro.
  3. Se aplica el operador
  4. Se obtiene un resultado (por ser simple, digamos en el primer registro)

Por ejemplo:

Reg A 2   00000010
Reg B 2   00000010
  + 4 00000100

Esto es fácil de entender, ya que estamos operando con números simples. Y así era, al principio. Números enteros, fáciles de operar.

Otro ejemplo clásico es la operatoria de lógica.

Digamos que tengo un valor de verdad (1) y un valor de falsedad (0) y operamos lógicamente. Esto es:

  AND OR
1 0 1
0 0 1
0 1

Por esto, cada cálculo se optimiza para los registros. En conclusión, los valores numéricos (enteros sobre todo), son los más rápidos y manejables.

Y las operaciones se hacen moviendo los valores de alguna parte de la memoria de trabajo, en los registros, ida y vuelta. Eso es por lo que se suele hablar de “tipos (de datos) por valor”.

En cambio, cuando no es posible utilizar esta operatoria, el procesador utiliza las referencias a los lugares en la memoria (direcciones), donde se encuentra los datos, y opera contra ellos. Más efectivo, pero mucho más lento. Entonces, ante estos datos, se realizan las operaciones pero con “punteros” que dirigen a la memoria (existen registros especiales del procesador para esto). Estos son, entonces, los “tipos por referencia”.

En el .Net Framework, tenemos entonces:

Tipos por Valor

Fe de e-ratas En la tabla, donde dice que char mide 1 byte, debiera decir 2 bytes. (Gracias Ivanov!). Es que son caracteres de doble byte, para que pueda almacenar cualquier idioma (por ejemplo, Klingon Klingon )

Tipos por valor:

Finalmente, existen algunos pocos tipos de datos definidos por el programador que heredan naturalmente de ValueType (no es posible declarar herencia explícita).

Enumeraciones

Un tipo de dato, que naturalmente hereda de alguno de los anteriores, pero que solo exponen algunos valores, por ejemplo

Visual Basic
Enum DíasSemana As SByte
    Domingo
    Lunes
    Martes
    Miércoles
    Jueves
    Viernes
    Sábado
End Enum

C#
    enum DíasSemana : sbyte
    {
        Domingo,
        Lunes,
        Martes,
        Miércoles,
        Jueves,
        Viernes,
        Sábado
    }

Estructuras

Son tipos definidos por el usuario que admiten varios campos en la definición. Llamativamente, heredan de ValueType aun cuando en su definición contengan tipos por referencia. Sin embargo, esos tipos por referencia solo tienen la dirección de OTRA posición de memoria donde se almacena dicho objeto.

Visual  Basic
Structure Estructura
    Public Entero As Integer
    Public Lógico As Boolean
    Public cadena As String
End Structure

C#
    struct Estructura
    {
        public int Entero;
        public bool Lógico;
        public string cadena;
    }

Los tipos por referencia

Hablamos de tipos de datos por referencia, cuando el procesador debe “saltar” de sus registros (especiales para referencias), a la dirección de memoria donde está ubicado el dato y volver luego a seguir con lo suyo. Siendo este proceso más lento es, sin embargo, mucho más versátil.

Por otra parte, los datos por referencia nos permiten mucha mayor versatilidad ya que podemos almacenar información diferente, compuesta y compleja. Obviamente, en la mayoría de los procesos, estos tipos de datos son los más utilizados. Pero recordemos siempre que procesar tipos por valor es más rápido.

En la próxima hablaremos de tipos por referencia en detalle, objetos y… seguimos.