¿Cuáles son los usos clave de un Static Clase genérica en C#? ¿Cuándo deberían ellos ser utilizados? ¿Qué ejemplos ilustran mejor el uso de ?
Creo que, en general, debe evitar crear parámetros de tipo en las clases estáticas, de lo contrario no puede depender de la inferencia de tipos para hacer que su código de cliente sea más conciso.
Para dar un ejemplo concreto, digamos que está escribiendo una clase de utilidad estática para manejar las operaciones en las listas. Puede escribir la clase de dos maneras:
// static class with generics
public static class ListOps<T>
{
public static List<T> Filter(List<T> list, Func<T, bool> predicate) { ... }
public static List<U> Map<U>(List<T> list, Func<T, U> convertor) { ... }
public static U Fold<U>(List<T> list, U seed, Func<T, U> aggregator) { ... }
}
// vanilla static class
public static class ListOps
{
public static List<T> Filter<T>(List<T> list, Func<T, bool> predicate) { ... }
public static List<U> Map<T, U>(List<T> list, Func<T, U> convertor) { ... }
public static U Fold<T, U>(List<T> list, U seed, Func<T, U> aggregator) { ... }
}
Pero las clases son equivalentes, pero ¿cuál es más fácil de usar? Compare:
// generic static class
List<int> numbers = Enumerable.Range(0, 100).ToList();
List<int> evens = ListOps<int>.Filter(numbers, x => x % 2 = 0);
List<string> numberString = ListOps<int>.Map(numbers, x => x.ToString());
int sumOfSquares = ListOps<int>.Fold(numbers, 0, (acc, x) => acc + x*x);
// vanilla static class
List<int> numbers = Enumerable.Range(0, 100).ToList();
List<int> evens = ListOps.Filter(numbers, x => x % 2 = 0);
List<string> numberString = ListOps.Map(numbers, x => x.ToString());
int sumOfSquares = ListOps.Fold(numbers, 0, (acc, x) => acc + b * b);
En mi opinión, ListOps<someType>
es voluminoso y torpe. La definición de la clase vainilla es un poco más grande, pero el código del cliente es más fácil de leer, gracias a la inferencia de tipo.
En el peor de los casos, C# no puede inferir los tipos, y debe especificarlos a mano. ¿Prefieres escribir ListOps<A>.Map<B>(...)
o ListOps.Map<A, B>(...)
? Mi preferencia es hacia este último.
La estrategia anterior funciona particularmente bien cuando su clase estática mantiene ningún estado mutable, o su estado mutable es conocido en tiempo de compilación .
Si la clase estática tiene un estado mutable cuyo tipo no se determina en tiempo de compilación, entonces probablemente tenga un caso de uso para una clase estática con params genéricos. Afortunadamente, estas ocasiones son pocas y distantes, pero cuando sucedan, estarás feliz de que C# sea compatible con la funcionalidad.
sería informativo tener una muestra del constructor de creación de expresiones lambda estáticas como parte de la respuesta. – Jim