domingo, 31 de julio de 2016

Comparación de Orden en C# | Parte 2/2 | Operadores Menor Que y Mayor Que e Implementación Interfaces IComparable

Índice

1. Introducción
2. Palabras Clave
3. Operadores < y >
4. Implementación de las Interfaces IComparable
5. Conclusiones
6. Literatura & Enlaces

1. Introducción

En el artículo C# anterior se estudiaron los protocolos estándar de comparación de orden; en especial se habló acerca de las interfaces y operadores de C# que permiten la escritura de tipos de datos cuyos valores siguen un orden establecido. Esta segunda parte describe el contexto donde resulta práctico implementar las interfaces IComparable, y cuándo es viable la sobrecarga de los operadores < y >.

2. Palabras Clave

  • Comparación de orden
  • IComparable
  • IComparable<T>
  • Operador >
  • Operador <

3. Operadores < y >

Los tipos datos numéricos integrados -byte, int, float, double, decimal, etc.- tienen un orden natural. Este orden establece relaciones como 'menor que', 'mayor que', 'menor o igual que' o 'mayor o igual que'. En el lenguaje de programación C# el programador cuenta con operadores estándar para efectuar operaciones de comparación de orden a través de los operadores <, >, <= y >=.

Esta clase de relaciones no sólo está implementada para valores numéricos sino para otros tipos datos integrados de Microsoft .NET Framework. La estructura DateTime (cfr. Manipulación de Fechas y Horas en C#/5: La Estructura DateTime) sobrecarga las operadores < y > para comparar el orden de fechas.

Ejemplo de uso

bool despuesDe2019 = DateTime.Now > new DateTime(2019, 1, 1);
Console.WriteLine(despuesDe2019); // False

En este ejemplo el operador > compara la hora fecha actual con 2019/1/1. Se puede pensar que la sobrescritura de > tiene sentido debido a que la representación numérica de una fecha tiene un orden cronológico; inclusive se puede expresar en términos 'viene antes de' o 'viene después de'.

[Nota: El resultado de la evaluación de estas expresiones puede cambiar si este artículo es leído después 2019/1/1.]

Para lograr la comparación del orden de fechas, la estructura DateTime implementa las interfaces IComparable, como se muestra en la Figura 1
Implementación IComparable por DateTime
Figura 1. Implementación interfaces IComparable por DateTime ("DateTime Structure", 2016).
Continuando, en Albahari J. (2012) se anuncia que la sobrecarga de los operadores < y > involucra la implementación de las interfaces IComparable es una práctica estándar; sin embargo, la operación inversa no resulta verdadera. En .NET Framework la gran mayoría de tipos que implementan las interfaces IComparable no sobrecargan los operadores de orden mencionados.


La clase System.String constituye un ejemplo evidente para este último caso: 
Comparación en objetos String
Figura 2. Comparación errónea en objetos String.
Aún vale agregar que para la comparación de igualdad, siempre que se sobrecarga el método Equals, es una práctica estándar sobrecargar el operador de igualdad de C# == (Albahari J., 2012).

¿En qué situaciones resulta típico sobrecargar los operadores < y >?: 
  • El tipo de dato en consideración inherentemente considera los conceptos de 'mayor que' y 'menor que' para los valores que representa.
  • El resultado de la evaluación es independiente de la cultura.
  • La comparación de orden sólo tiene un único sentido o contexto para llevarse a cabo.

4. Implementación de las Interfaces IComparable

El siguiente ejemplo demuestra cómo implementar los protocolos estándar de comparación de orden discutidos anteriormente. El ejemplo compara semitonos de una nota musical.


Ejemplo de uso

Archivo C# NotaMusical.cs [Enlace alternativo][Enlace alternativo]: 

Líneas sobresalientes de este ejemplo de código fuente C#
  1. Líneas 37-46: Implementa el método CompareTo de la interfaz genérica IComparable<NotaMusical>. Dentro de este método se comprueba que si dos objetos del tipo paramétrico son iguales, entonces ambos ocupan el mismo lugar de orden. En caso contrario se efectúa la comparación con la implementación de CompareTo para valores de int de la propiedad miembro de esta estructura.
  2. Líneas 49-58: Implementa el método CompareTo de la versión no genérica de la interfaz IComparable. Primero se comprueba que si el tipo de dato es de tipo NotaMusical; de no ser así se genera la excepción InvalidOperationException. Luego la comparación se efectúa con la versión genérica de CompareTo (línea 57).
  3. Líneas 69-78: Se sobrecarga los operadores < y >. Nótese que se usan el método CompareTo y el operador == (más adelante sobrecargado). Esto garantiza la consistencia de comparación de igualdad y comparación de valores.
  4. Líneas 75-78: Sobrecarga el método Equals de la interfaz genérica IEquatable<NotaMusical>. Aquí también se usa el operador == sobrecargado en esta misma estructura. También se sobrescribe Object.Equals para mantener la consistencia con esta versión genérica.
  5. Líneas 100-110: Sobrecarga de los operadores == y != para la comparación de igualdad de objetos NotaMusical.
Salida resultante de la ejecución de este código en LINQPad

nm1 == nm2 :                  False
nm1.Equals((object)nm2): False
nm1 == nm3 :                  True
nm1.Equals((object)nm2): True
nm1 < nm2:                     True
nm1 != nm2:                    True

5. Conclusiones

Se demostró a través de un ejemplo cómo el programador puede implementar las interfaces IComparable (no-genérica y genérica) y IEquatable<T> para definir una semántica natural tanto de comparación de igualdad y de orden para el tipo de dato NotaMusical. El poder que entrega este conocimiento al programador consiste en crear tipo de datos robustos y acordes a un dominio real.

Este capítulo de artículos C# -Capítulo 6: Fundamentos de .NET Frameworkterminará con la exposición de las clases de utilidad disponibles en C#.

6. Literatura & Enlaces

Albahari, J., Albahari, B. (2012). C# 5.0 in a Nutshell. United States: O'Reilly Media.
Manipulación de Fechas y Horas en C#/5: La Estructura DateTime (2016, julio 31). Recuperado desde: https://ortizol.blogspot.com/2016/06/manipulacion-de-fechas-y-horas-en-csharp-parte-2-5-la-estructura-datetime.html
DateTime Structure (System) (2016, julio 31). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.datetime.aspx
Semitono, la enciclopedia libre (2016, julio 31). Recuperado desde: https://es.wikipedia.org/wiki/Semitono


O

sábado, 30 de julio de 2016

Comparación de Orden en C# | Parte 1/2 | Introducción a Protocolos Estándar de Comparación de Orden

Índice

1. Introducción
2. Palabras Clave
3. Protocolos Estándar de Comparación de Orden
4. Las Interfaces IComparable
5. IComparable vs. Equals
6. Conclusiones
7. Literatura & Enlaces

1. Introducción

En la serie C# anterior se estudiaron los fundamentales acerca de los protocolos de comparación de igualdad; C# de .NET cuenta además con protocolos estándar para para la determinación del orden de un objeto respecto a otro. Esta serie, compuesta de dos partes, estudia los básicos de esta nueva clase de protocolos estándar:
  1. Interfaces IComparable y IComparable<T>
  2. Operadores de comparación < y > e implementación interfaces IComparable
La primera parte explica los conceptos básicos de la comparación de orden, y se explica cuál es el propósito de las interfaces IComparable (no-genérica) y IComparable<T> como protocolos estándar de comparación de orden.

2. Palabras Clave

  • Comparación de orden
  • IComparable
  • IComparable<T>
  • Protócolo estándar

3. Protocolos Estándar de Comparación de Orden

Así como .NET Framework contiene protocolos estándar para la definición de la semántica de igualdad, también define un conjunto de protocolos estándar para la determinación del orden que ocupa un objeto respecto a otro (Albahari J., 2012).

Los protocolos estándar se han definido como interfaces y operadores de C#; exactamente: 
  • Las interfaces IComparable
    • IComparable (versión no genérica)
    • IComparable<T> (versión genérica)
  • Los operadores de C# < y >
En cuanto a las interfaces, éstas se utilizan como algoritmos de ordenamiento de propósito general (Albahari J., 2012).

En .NET existe una variedad de clases, como por ejemplo String, que implementa una de estas interfaces para el ordenamiento de arreglo de cadenas de caracteres; por ejemplo: 

Ejemplo de uso

string[] escritoresRusos = { "Asimov", "Dostoiévskiy", "Gógol", "Lermontov", "Púshkin"};
Array.Sort(escritoresRusos);

foreach (string escritorRuso in escritoresRusos)
{
Console.Write(escritorRuso + " ");
}

En la salida estándar de LINQPad este es el resultado obtenido: 

Asimov Dostoiévskiy Gógol Lermontov Púshkin 

En cuanto a los operadores < y >, éstos han sido especialmente diseñados para tipos de datos numéricos. Al igual que ocurre con los operadores == y !=, los operadores < y > son resueltos de manera estática, lo que se traduce en un alto desempeño en algoritmos que efectúan miles de operaciones por segundo (Albahari J., 2012).

4. Las Interfaces IComparable

Entre los protocolos estándar de comparación de orden que provee .NET se hallan: 
  • IComparable
  • IComparable<T>
El contrato (versión simplificada) de estas interfaces comprende: 

public interface IComparable
{
int CompareTo(object other);
}

public interface IComparable
{
int CompareTo(T other);
}

Para la comparación de tipos de datos numéricos la segunda versión de IComparable resulta más eficiente debido a que se evita el uso de la técnica boxing para efectuar la comparación de orden entre dos valores. Aún así, las dos versiones siguen este esquema de funcionamiento en la implementación del método CompareTo
  • Si x está después de y, entonces x.CompareTo(y) retorna un valor numérico positivo.
  • Si x ocupa el mismo lugar de y, entonces x.CompareTo(y) retorna 0.
  • Si x está antes de y, entonces x.CompareTo(y) retorna un valor numérico negativo.
Para cadenas de texto, por ejemplo se podría realizar estas operaciones de comparación de orden:

Ejemplo de uso:

Console.WriteLine("Dostiévskiy".CompareTo("Asimov")); // 1
Console.WriteLine("Dostiévskiy".CompareTo("Dostiévskiy")); // 0
Console.WriteLine("Asimov".CompareTo("Lermontov")); // -1

Sobre string se puede efectuar esta comparación debido a que ésta clase implementa la interfaz IComparable<T>.

5. IComparable vs. Equals

Considérese la siguiente situación: se ha escrito un tipo de dato personalizado que sobrescribe el método virtual Equals y que implementa el método abstract de una de las interfaces IComparable.

Ahora, se puede pensar en la siguiente pregunta: ¿qué ocurre al comparar e igualar dos valores del tipo de dato en cuestión? Bueno, si los dos valores son iguales, Equals retornará True, y el método CompareTo retornará 0. Por otra parte, mientras que Equals retorna False cuando los dos valores comparados por igualdad son diferentes, CompareTo retorna -1 o 1 dependiendo del orden qué ocupen los valores.

Para ilustrar esto, la clase String constituye un buen ejemplo: en algunos sistemas las cadenas "ṻ" y "ǖ" son distintas para el método Equals pero equivalentes para el método CompareTo. Esto es así porque CompareTo es más meticuloso en cuanto a comparaciones que dependen de la cultura y la configuración regional del sistema.

Ejemplo de uso

Console.WriteLine("".Equals("ǖ")); // True
Console.WriteLine("".CompareTo("ǖ")); // 0

Otro punto importante a considerar es la recomendación dada en Albahari J. (2012)
Primera línea en CompareTo
Figura 1. Primera línea en CompareTo.
En términos generales se está sugiriendo evaluar primero la igualdad de los valores, y luego realizar la comparación de orden de acuerdo a la naturaleza del tipo de dato.

6. Conclusiones

Se estudiaron los básicos de los protocolos estándar de comparación de orden. Se comprendió que la comparación de orden al igual que la comparación de igualdad presenta dificultades y/o complejidades que deben ser comprendidas rigurosamente para evitar comportamientos o resultados anómalos.

La siguiente parte de esta serie explica el contexto de uso de los operadores de comparación < y >; además, un ejemplo de implementación de las interfaces IComparable e IComparable<T>.

7. Literatura & Enlaces

Albahari, J., Albahari, B. (2012). C# 5.0 in a Nutshell. United States: O'Reilly Media.

O

viernes, 29 de julio de 2016

Comparación de Igualdad en C# | Parte 5/5 | Personalización de los Tipos de Igualdad: Sobrecarga de Operadores == y !=, y Ejemplo de Implementación de IEquatable

Índice

1. Introducción
2. Palabras Clave
3. Sobrecarga Operadores == y !=
4. Implementación de la Interfaz IEquatable<T>
4.1 Generalidades
4.2 Ejemplo de uso
4.3 Comparadores de igualdad enchufables
5. Conclusiones
6. Literatura & Enlaces

1. Introducción

Esta serie finaliza con el tema de sobrecarga operadores de igualdad de C#: == y !=, la guía básica de implementación y referencia de la interfaz IEquatable<T>, y al final un ejemplo que demuestra cómo construir la estructura Area, la cual implementa IEquatable<T>: la idea principal es ilustrar un caso donde la semántica de comparación de igualdad tiene un sentido de contexto particular.

2. Palabras Clave

  • Comparación de igualdad
  • Estructura
  • Interfaz IEquatable<T>
  • Operador ==
  • Operador !=
  • Semántica de equivalencia
  • Sobrecarga

3. Sobrecarga Operadores == y !=

En el artículo anterior -Comparación de Igualdad en C# | Parte 4/5 | Igualdad y Tipos de Datos Personalizados: Aspectos Generales-, se estudió el proceso de sobrescritura del método Equals para cambiar la semántica de la comparación de igualdad para un tipo de dato específico; de manera análoga, el programador puede sobrecargar los operadores == y != para crear una lógica de comparación de igualdad para tipos de datos struct.

Para clases existen dos maneras de proceder (Albahari J., 2012)
  • No modificar el comportamiento de == y != de tal forma que se conserve la comparación de igualdad referencial.
  • Sobrecargar los operadores == y != acorde con el método Equals. [Esto se demuestra con la sección 4.2.]
Por ejemplo para la clase StringBuilder, los operadores == y != siguen una semántica de igualdad como en otros tipos de datos por referencia; esto no es así con Equals: compara el contenido literal de la instancia.

En el siguiente ejemplo de uso se demuestra cómo los operadores en cuestión aún se usan para comparar referencias y el método Equals ha sido sobrescrito para comparar valores y no referencias a objetos: 

Ejemplo de uso

var sb1 = new StringBuilder("Uniandes");
var sb2 = new StringBuilder("Uniandes");

Console.WriteLine(sb1 == sb2); // Fasle - Igualdad por referencia
Console.WriteLine(sb1.Equals(sb2)); // True - Igualdad por valor

Es evidente que se ha de entender correctamente el concepto de equivalencia acorde a la naturaleza del tipo de dato. Algunas preguntas a responder para determinar su semántica de comparación de igualdad podrían ser: 
  • ¿Cuál es la manera más natural de efectuar la comparación entre los valores de dos entidades del mismo tipo?
  • ¿Cuál de las alternativas consideradas resulta menos ambigua?

4. Implementación de la Interfaz IEquatable<T>

4.1 Generalidades

En la sección 4 del artículo Comparación de Igualdad en C# | Parte 3/5 | Protocolos de Igualdad Estándar: Método static object.ReferenceEquals e Interfaz IEquatable se describe los básicos sobre esta interfaz y un ejemplo introductorio de su uso. Sin embargo, vale reiterar que gracias a su propiedad de genericidad no es requerido efectuar la técnica de boxing, lo que si ocurre con el método Object.Equals(Object) (Albahari J., 2012).

4.2. Ejemplo de uso

Este ejemplo de uso demuestra cómo implementar una estructura para la representación del área con medidas intercambiables: altura por ancho es equivalente ancho por altura.

Ejemplo de uso

Líneas a resaltar de este código: 
  • Líneas 27-39: Sobrescribe el método Object.Equals(Object). Primero se evalúa que el argumento corresponde con una instancia de Area; de no ser así, se retorna false, en caso contrario se invoca la implementación de Equals(Area).
  • Líneas 42-46: Implementa el método Equals de la interfaz IEquatable<T>. Aquí se comprueba la igualdad de cada uno de los campos de la estructura: Medida1 y Medida2.
Además de esta sobrescritura e implementación del método Equals de la interfaz IEquatable<T>, se sobrescribe los operadores == y !=
  1. Líneas 55-58: Para la comparación de igualdad de dos instancias con el operador ==, se invoca al método Equals implementado de la interfaz mencionada.
  2. Líneas 62-65: Análogo al anterior; para la comparación de diferencia, el operador != es sobrecargado.
Una vez ejecutado este código en LINQPad, este es el resultado obtenido: 

False
True
True
True

4.3 Comparadores de igualdad enchufables

En las series próximas de artículos dedicadas al trabajo con colecciones se introducirá la interfaz IEqualityComaparer. Esta interfaz es útil para especificar una semántica de igualdad distinta para escenarios específicos.

5. Conclusiones

Esta serie ha resultado de importante estudio para comprender los conceptos elementales detrás de la comparación de igualdad. Futuras publicaciones entrarán en más detalle en este interesante y retador tema. Ahora resta al programador considerar ir más a fondo y explorar la oportunidad que puede hallarse a la hora de escribir tipos de datos consistentes con un contexto de realidad específico.

La próxima serie de artículos C# describe la comparación de orden.

6. Literatura & Enlaces

Albahari, J., Albahari, B. (2012). C# 5.0 in a Nutshell. United States: O'Reilly Media.
Comparación de Igualdad en C# | Parte 4/5 | Igualdad y Tipos de Datos Personalizados: Aspectos Generales (2016, julio 29). Recuperado desde: https://ortizol.blogspot.com/2016/07/comparacion-de-igualdad-en-csharp-parte-4-5-igualdad-y-tipos-de-datos-personalizados-aspectos-generales.html
Comparación de Igualdad en C# | Parte 3/5 | Protocolos de Igualdad Estándar: Método static object.ReferenceEquals e Interfaz IEquatable (2016, julio 29). Recuperado desde: https://ortizol.blogspot.com.co/2016/07/comparacion-de-igualdad-en-csharp-parte-3-5-protocolos-de-igualdad-estandar-metodo-static-object.referenceequals-e-interfaz-iequatable-t.html


O