Cómo usar la inmutabilidad en C #

La inmutabilidad es una característica de los lenguajes de programación funcional que hace que los programas sean más fáciles de escribir, probar y mantener. Sin embargo, muchos lenguajes de programación imperativos no admiten la inmutabilidad. Hasta hace poco, C # no admitía la inmutabilidad lista para usar. 

Eso cambia con la introducción de registros en C # 9, que está disponible para vista previa en .NET 5. Sin embargo, podemos implementar la inmutabilidad en versiones anteriores de C # usando el espacio de nombres System.Collections.Immutable, que está disponible como un paquete NuGet. 

Un objeto inmutable se define como un objeto que no se puede cambiar después de haber sido creado. Para muchos casos de uso, como los objetos de transferencia de datos, la inmutabilidad es una característica deseable. En este artículo se explica por qué es posible que deseemos aprovechar la inmutabilidad y cómo podemos implementar la inmutabilidad en C #.

Para trabajar con los ejemplos de código proporcionados en este artículo, debe tener Visual Studio 2019 instalado en su sistema. Si aún no tiene una copia, puede descargar Visual Studio 2019 aquí. 

Crear un proyecto de aplicación de consola .NET Core en Visual Studio

En primer lugar, creemos un proyecto de aplicación de consola .NET Core en Visual Studio. Suponiendo que Visual Studio 2019 esté instalado en su sistema, siga los pasos que se describen a continuación para crear un nuevo proyecto de aplicación de consola .NET Core en Visual Studio.

  1. Inicie el IDE de Visual Studio.
  2. Haga clic en "Crear nuevo proyecto".
  3. En la ventana "Crear nuevo proyecto", seleccione "Aplicación de consola (.NET Core)" en la lista de plantillas que se muestran.
  4. Haga clic en Siguiente. 
  5. En la ventana "Configure su nuevo proyecto" que se muestra a continuación, especifique el nombre y la ubicación del nuevo proyecto.
  6. Haga clic en Crear. 

Esto creará un nuevo proyecto de aplicación de consola .NET Core en Visual Studio 2019. Usaremos este proyecto para ilustrar la inmutabilidad en las siguientes secciones de este artículo.

Instale el paquete System.Collection.Immutable NuGet

Para trabajar con tipos inmutables, debe instalar el paquete System.Collections.Immutable de NuGet. Puede hacerlo a través del administrador de paquetes NuGet dentro del IDE de Visual Studio 2019 o ejecutando el siguiente comando en la consola del administrador de paquetes NuGet:

Paquete de instalación System.Collections.Immutable

Este paquete comprende una colección de clases seguras para subprocesos, también conocidas como colecciones inmutables.

Comprender la inmutabilidad y los registros en C # 9

Un objeto de transferencia de datos es un ejemplo clásico de cuándo desea inmutabilidad. Una instancia de un DTO a menudo se serializa para que pueda ser independiente de la tecnología utilizada por el consumidor. Naturalmente, al transferir un objeto de datos entre una base de datos y un cliente, le gustaría asegurarse de que el objeto no se pueda cambiar, y ese es exactamente el propósito de un DTO. Puede leer más sobre el uso de objetos de transferencia de datos en C # en mi artículo anterior aquí. 

Para crear DTO inmutables, puede aprovechar ReadOnlyCollection o los tipos de colección inmutables seguros para subprocesos en el espacio de nombres System.Collections.Immutable. Alternativamente, puede aprovechar los tipos de registro en C # 9 para implementar DTO inmutables.

Un tipo de registro en C # 9 es un tipo de datos ligero e inmutable (o una clase ligera) que tiene propiedades de solo lectura. Dado que un tipo de registro es inmutable, es seguro para subprocesos y no puede mutar ni cambiar una vez creado.

Puede inicializar un tipo de registro solo dentro de un constructor. Crear un tipo de registro para una clase (Autor en este ejemplo) es tan simple como el siguiente fragmento de código.

datos de clase Autor (int Id, string firstName, string lastName, string address);

También puede escribir el tipo de registro de Autor como se muestra en el fragmento de código que se proporciona a continuación:

clase de datos públicos Autor {

    public int Id {obtener; en eso; }

    cadena pública firstName {get; en eso; }

    cadena pública lastName {get; en eso; }

    dirección de cadena pública {get; en eso; }

}

Tenga en cuenta el uso de la palabra clave de datos al declarar el tipo de registro. La palabra clave de datos cuando se usa en la declaración de una clase marca el tipo como un registro. Puede aprovechar una instancia de tipo de registro para pasar datos a través de las capas y, al mismo tiempo, garantizar la inmutabilidad de los DTO.

El espacio de nombres System.Collections.Immutable

Las colecciones inmutables son aquellas cuyos miembros no pueden cambiar una vez que se han creado. El espacio de nombres System.Collections.Immutable comprende varias colecciones inmutables. Este espacio de nombres contiene versiones inmutables de listas, diccionarios, matrices, hashes, pilas y colas.

ImmutableStack se puede usar para empujar y hacer pop elementos de la misma manera que lo hacemos con las pilas mutables. Sin embargo, dado que ImmutableStack es una colección inmutable, sus elementos no se pueden alterar. Entonces, cuando realiza una llamada al método pop para extraer un elemento de la pila, se crea una nueva pila para usted y la pila original permanece inalterada.

Ilustremos esto con un ejemplo. El siguiente fragmento de código muestra cómo puede insertar elementos en una pila inmutable.

var stack = ImmutableStack.Empty;

para (int i = 0; i <10; i ++)

{

    stack = stack.Push (i);

}

El siguiente programa demuestra que los elementos de una pila inmutable no se pueden alterar.

programa de clase

    {      

        static void Main (cadena [] argumentos)

        {

            var stack = ImmutableStack.Empty;

            para (int i = 0; i <10; i ++)

            {

                stack = stack.Push (i);

            }

            Console.WriteLine ("No de elementos en la pila original:

             "+ stack.Count ());

            var newStack = stack.Pop ();

            Console.WriteLine ("No de elementos en la nueva pila:" +

            newStack.Count ());

            Console.ReadKey ();

        }

    }

Cuando ejecuta el programa anterior, así es como debería aparecer la salida en la ventana de la consola.

Como puede ver en la Figura 1, la pila inmutable original (que contiene 10 elementos) no cambia después de una llamada al método Pop (). Más bien, se crea una nueva pila inmutable con 9 elementos.

Las colecciones inmutables no ofrecen constructores, pero puede aprovechar el método de fábrica estático llamado Crear, como se muestra en el fragmento de código que se muestra a continuación.

var list = ImmutableList.Create (1, 2, 3, 4, 5);

Si quisiera agregar o eliminar un elemento de esta colección, se crearía una nueva lista inmutable y la lista inmutable original permanecería sin cambios.

La inmutabilidad es una elección de diseño; significa que una instancia de un tipo no se puede cambiar después de haber sido creada. A excepción de las pilas inmutables y las colas inmutables, todas las colecciones inmutables se basan en árboles AVL. Por lo tanto, puede insertar elementos en cualquier posición de la colección, es decir, el principio, el medio o el final, sin necesidad de copiar el árbol en su totalidad.

Cómo hacer más en C #:

  • Cómo usar anotaciones de datos en C #
  • Cómo trabajar con GUID en C # 8
  • Cuándo usar una clase abstracta frente a una interfaz en C #
  • Cómo trabajar con AutoMapper en C #
  • Cómo usar expresiones lambda en C #
  • Cómo trabajar con delegados Action, Func y Predicate en C #
  • Cómo trabajar con delegados en C #
  • Cómo implementar un registrador simple en C #
  • Cómo trabajar con atributos en C #
  • Cómo trabajar con log4net en C #
  • Cómo implementar el patrón de diseño del repositorio en C #
  • Cómo trabajar con la reflexión en C #
  • Cómo trabajar con filesystemwatcher en C #
  • Cómo realizar la inicialización diferida en C #
  • Cómo trabajar con MSMQ en C #
  • Cómo trabajar con métodos de extensión en C #
  • Cómo usar expresiones lambda en C #
  • Cuándo usar la palabra clave volátil en C #
  • Cómo usar la palabra clave yield en C #
  • Cómo implementar polimorfismo en C #
  • Cómo construir su propio programador de tareas en C #
  • Cómo trabajar con RabbitMQ en C #
  • Cómo trabajar con una tupla en C #
  • Explorando métodos virtuales y abstractos en C #
  • Cómo usar Dapper ORM en C #
  • Cómo usar el patrón de diseño flyweight en C #