Bienvenidos a la continuación del lanzamiento del .NET Community Toolkit. En esta segunda parte, nos sumergimos aún más en las características y herramientas que este conjunto proporciona a la comunidad de desarrolladores. Descubriremos cómo el Toolkit potencia la productividad y la creatividad, ofreciendo soluciones innovadoras y eficientes para los desafíos comunes en el desarrollo de aplicaciones .NET. Desde mejoras en la interfaz de usuario hasta utilidades esenciales, exploraremos en detalle las gemas que hacen del .NET Community Toolkit una valiosa adición a tu conjunto de recursos. Únete a nosotros en este viaje de descubrimiento mientras destacamos las emocionantes funcionalidades que enriquecen el desarrollo .NET en la comunidad.
Compatibilidad con cambios Broadcast para propiedades generadas
Se ha añadido un nuevo atributo [NotifyPropertyChangedRecipients], que se puede utilizar en la propiedad observable generada a partir de un tipo que hereda de ObservableRecipient (o que está anotado con [ObservableRecipient]). Al emplearlo, se generará una llamada al método Broadcast para enviar un mensaje a todos los demás componentes suscritos sobre el cambio de propiedad que acaba de ocurrir. Esto puede ser útil en situaciones donde un cambio en la propiedad de un modelo de vista también debe notificarse a otros componentes de la aplicación (por ejemplo, supongamos que hay una propiedad booleana IsLoggedIn que se actualiza cuando un usuario inicia sesión; esto puede notificar y desencadenar que algunos otros componentes de la aplicación se actualicen con el mensaje difundido).
Se usa:
[ObservableProperty] [NotifyPropertyChangedRecipients] private string name;
Produce:
public string Name { get => name; set { if (!EqualityComparer<string>.Default.Equals(name, value)) { OnNameChanging(value); OnPropertyChanging(); string oldValue = name; name = value; Broadcast(oldValue, value, nameof(Name)); OnNameChanged(); OnPropertyChanged(); } } }
Composición de ViewModel
Debido a la ausencia de herencia múltiple en C#, a veces esto puede suponer un desafío.
¿Qué ocurre si hay un modelo de vista que debe heredar de un tipo específico, pero al mismo tiempo desea incorporar compatibilidad con INotifyPropertyChanged o hacer que también heredé de ObservableRecipient para acceder a sus API?
El kit de herramientas de MVVM ha implementado una solución para este problema mediante la introducción de atributos para la generación de código, lo que facilita la incorporación de lógica de estos tipos en clases arbitrarias. Dichos atributos son [INotifyPropertyChanged], [ObservableObject] y [ObservableRecipient].
Al aplicar estos atributos a una clase, el generador de código fuente de MVVM Toolkit incorporará toda la lógica de ese tipo en esa clase, como si dicha clase también hubiera heredado de ese tipo. Por ejemplo:
[INotifyPropertyChanged] partial class MyObservableViewModel : DatabaseItem { }
Esto heredará de MyObservableViewModel como era de esperar, pero el uso de también le permitirá obtener soporte para [INotifyPropertyChanged], junto con todas las API auxiliares incluidas por ObservableObject.
Se sigue recomendando heredar de los tipos base, como en el caso de ObservableObject, ya que esto también puede contribuir a la reducción del tamaño binario. No obstante, la capacidad de insertar código de esta manera cuando sea necesario puede ser útil para superar las limitaciones de C# en situaciones en las que no es posible cambiar el tipo base de un modelo de vista, como se ejemplifica anteriormente.
Mensajería mejorada en APIs
Otra característica de uso común en MVVM Toolkit es la interfaz IMessenger, que representa un contrato para tipos que se pueden utilizar para intercambiar mensajes entre diferentes objetos. Esto puede resultar beneficioso para desacoplar varios módulos de una aplicación sin la necesidad de mantener referencias directas a los tipos a los que se hace referencia. Además, es posible enviar mensajes a canales específicos, identificados de manera única por un token, y tener distintos mensajeros en diversas secciones de una aplicación.
MVVM Toolkit proporciona dos implementaciones de esta interfaz:
- WeakReferenceMessenger: que no rootea a los destinatarios y permite recopilarlos. Esto se implementa a través de identificadores dependientes, que son un tipo especial de referencias de GC que permiten a este mensajero asegurarse de que siempre se recopilan los destinatarios registrados, incluso si un controlador registrado hace referencia a ellos, pero no existen otras referencias seguras pendientes a ellos.
- StrongReferenceMessenger: que es una implementación de mensajería que rootea a los destinatarios registrados para garantizar que permanezcan vivos incluso si el mensajero es el único objeto que hace referencia a ellos.
Ejemplo:
// Declare a message public sealed record LoggedInUserChangedMessage(User user); // Register a recipient explicitly... messenger.Register<MyViewModel, LoggedInUserChangedMessage>(this, static (r, m) => { // Handle the message here, with r being the recipient and m being the // input message. Using the recipient passed as input makes it so that // the lambda expression doesn't capture "this", improving performance. }); // ...or have the viewmodel implement IRecipient<TMessage>... class MyViewModel : IRecipient<LoggedInUserChangedMessage> { public void Receive(LoggedInUserChangedMessage message) { // Handle the message here } } // ...and then register through the interface (other APIs are available too) messenger.Register<LoggedInuserChangedMessage>(this); // Send a message from some other module messenger.Send(new LoggedInUserChangedMessage(user));
Las implementaciones de mensajería en esta nueva versión del kit de herramientas de MVVM han experimentado una significativa optimización en .NET 6 gracias a la incorporación de la nueva API pública DependentHandle, permitiendo que los tipos de mensajería sean aún más eficaces y proporcionen una difusión de mensajes completamente sin asignación. A continuación, se detallan algunos puntos de referencia que ilustran el rendimiento de los mensajeros del kit de herramientas de MVVM en comparación con otros tipos equivalentes de otras bibliotecas de MVVM ampliamente utilizadas:
Método | Significar | Error | StdDev | Proporción | RatioSD | Gen 0 | Generación 1 | Asignado |
MVVMToolkitStrong | 4.025 ms | 0,0177 ms | 0,0147 ms | 1.00 | 0.00 | – | – | – |
MVVMToolkitDébil | 7.549 ms | 0,0815 ms | 0,0762 ms | 1.87 | 0.02 | – | – | – |
MvvmCrossStrong | 11.483 ms | 0,0226 ms | 0,0177 ms | 2.85 | 0.01 | 9687.5000 | – | 41.824.022 B |
MvvmCrossDébil | 13.941 ms | 0,1865 ms | 0,1744 ms | 3.47 | 0.04 | 9687.5000 | – | 41.824.007 B |
MVVMLight | 52.929 ms | 0,1295 ms | 0,1011 ms | 13.14 | 0.06 | 7600.0000 | – | 33.120.010 B |
Estilete | 91.540 ms | 0,6362 ms | 0,4967 ms | 22.73 | 0.17 | 35500.0000 | – | 153.152.352 B |
MvvmGen | 141,743 ms | 2,7249 ms | 2,7983 ms | 35.31 | 0.70 | 19250.0000 | – | 83.328.348 B |
Catel | 148.867 ms | 2,6825 ms | 2,5093 ms | 36.94 | 0.64 | 5250.0000 | – | 22.736.316 B |
Prisma | 150.077 ms | 0,5359 ms | 0,4184 ms | 37.26 | 0.13 | 17500.0000 | 250.0000 | 76.096.900 B |
CaliburnMicro | 280,740 ms | 3,7625 ms | 3,1418 ms | 69.74 | 0.82 | 88000.0000 | 2000.0000 | 381.859.608 B |
MauiMessagingCenter | 673,656 ms | 1,7619 ms | 1,3755 ms | 167.26 | 0.63 | 8000.0000 | – | 35.588.776 B |
Cada prueba comparativa implica enviar 4 mensajes diferentes 1000 veces a 100 destinatarios. Como se evidencia, WeakReferenceMessenger y StrongReferenceMessenger destacan considerablemente en velocidad, siendo los únicos que no generan ninguna asignación de memoria al transmitir mensajes.
API Renovadas
En esta nueva iteración del kit de herramientas de MVVM, se trasladan todos los tipos de colecciones agrupadas observables desde el paquete CommunityToolkit.Common hasta CommunityToolkit.Mvvm. Simultáneamente, se implementan cambios significativos para mejorar la superficie de la API y hacerla más útil en diversos escenarios. Estas API son especialmente valiosas al trabajar con elementos agrupados, como en la presentación de una lista de contactos. Además, ahora incluyen extensiones que simplifican en gran medida operaciones comunes, como insertar un elemento en la posición correcta dentro de un grupo (usando el comparador predeterminado o uno proporcionado) y crear un nuevo grupo si es necesario.
Aplicación de ejemplo MVVM Toolkit
Como complemento a la nueva versión, también hemos lanzado la aplicación de ejemplo en la Tienda Microsoft. Esta aplicación incluye toda la documentación disponible en MS Docs, junto con ejemplos interactivos para muchas de las API disponibles. Su propósito es ser un recurso adicional para el kit de herramientas MVVM, y esperamos que sea de ayuda para aquellos que están comenzando a utilizar esta biblioteca.
API de diagnóstico mejoradas
El paquete CommunityToolkit.Diagnostics también ha experimentado mejoras significativas, aprovechando las nuevas características de expresión de argumento de autor de llamada y controlador de cadenas interpoladas de C# 10. Ahora, varias API que antes requerían un ahora también admiten un controlador personalizado. Esto permite que los sitios de llamadas eviten por completo el paso de interpolación si no se produce ninguna excepción, y ya no es necesario indicar manualmente el nombre del argumento.
Comparación:
// Diagnostics 7.1 public static void SampleMethod(int[] array, int index, Span<int> span, string text) { Guard.IsNotNull(array, nameof(array)); Guard.HasSizeGreaterThanOrEqualTo(array, 10, nameof(array)); Guard.IsInRangeFor(index, array, nameof(index)); Guard.HasSizeLessThanOrEqualTo(array, span, nameof(span)); Guard.IsNotNullOrEmpty(text, nameof(text)); } // Diagnostics 8.0 public static void SampleMethod(int[] array, int index, Span<int> span, string text) { Guard.IsNotNull(array); Guard.HasSizeGreaterThanOrEqualTo(array, 10); Guard.IsInRangeFor(index, array); Guard.HasSizeLessThanOrEqualTo(array, span); Guard.IsNotNullOrEmpty(text); }
Compatibilidad con .NET 6
La última edición de .NET Community Toolkit también introduce compatibilidad con .NET 6 como un nuevo destino en todas las bibliotecas disponibles. Esto implica diversas mejoras al ejecutarse en el entorno de ejecución de .NET más actual:
- La compatibilidad con el recorte ahora está habilitada para todas las bibliotecas. Para respaldar esto, todos los paquetes también tienen anotaciones de recorte completas para todas las API, para garantizar que todo sea compatible con el enlazador o que muestre explícitamente las advertencias correctas en tiempo de compilación (por ejemplo, este es el caso de algunas API de validación en MVVM Toolkit, que usan algunas API de la BCL que inherentemente necesitan alguna reflexión para funcionar).
- La extensión del paquete HighPerformance ahora también es compatible con y .Count<T>()nintnuint
- Se han introducido otras optimizaciones en todos los paquetes en .NET 6.
Por supuesto, todas las bibliotecas seguirán siendo compatibles con .NET Standard 2.0, lo que significa que también puede seguir haciéndoles referencia desde proyectos con diferentes marcos de destino. Y gracias a la lógica de resolución de paquetes NuGet, si crea una biblioteca con cualquiera de estos paquetes y la orienta a una plataforma más antigua (por ejemplo, .NET Standard 2.0) y un consumidor la referencia desde un proyecto dirigido a una nueva versión de .NET (por ejemplo, .NET 6), seguirá obteniendo automáticamente la versión más optimizada de los ensamblados del kit de herramientas de la comunidad de .NET que esté disponible para ellos.
Conclusión
En esta segunda entrega sobre el .NET Community Toolkit, hemos profundizado en características adicionales que fortalecen el arsenal de herramientas disponibles para los desarrolladores en la plataforma .NET. Desde mejoras en la interfaz de usuario hasta utilidades esenciales, hemos explorado cómo este toolkit aborda de manera efectiva los desafíos comunes en el desarrollo. Esta exploración continúa destacando la versatilidad y la eficacia del toolkit en la comunidad de desarrolladores. Esperamos que estas nuevas revelaciones impulsen la creatividad y productividad de quienes confían en el .NET Community Toolkit como una herramienta valiosa y continua enriquecedora para el desarrollo en .NET.s