Quali sono gli usi chiave di una classe generica statica in C#? Quando dovrebbero essere usati ? Quali esempi illustrano al meglio il loro uso ?
Penso che in generale, si dovrebbe evitare di creare parametri di tipo su classi statiche, altrimenti non si può dipendere dall'inferenza di tipo per rendere più conciso il codice client.
Per fare un esempio concreto, supponiamo che stiate scrivendo una classe di utilità statica per gestire le operazioni sugli elenchi. Puoi scrivere la classe in due modi:
// 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) { ... }
}
Ma le classi sono equivalenti, ma quale è più facile da usare? Confronto:
// 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);
A mio parere, ListOps<someType>
è ingombrante ed impacciato. La definizione della classe vanilla è leggermente più grande, ma il codice client è più facile da leggere, grazie all'inferenza di tipo.
Nel caso peggiore, C# non può inferire i tipi e devi specificarli a mano. Preferiresti scrivere ListOps<A>.Map<B>(...)
o ListOps.Map<A, B>(...)
? La mia preferenza è verso quest'ultimo.
La strategia di cui sopra funziona particolarmente bene quando la classe statica detiene nessuno stato mutabile, o il suo stato mutevole è noto al momento della compilazione .
Se la classe statica mantiene lo stato mutabile il cui tipo non è determinato in fase di compilazione, allora probabilmente avete un caso d'uso per una classe statica con params generici. Speriamo che queste occasioni siano poche e lontane tra loro, ma quando accadrà, sarai felice C# supporta la funzionalità.
sarebbe utile avere un esempio dell'espressione lambda statica che crea il costruttore come parte della risposta. – Jim