Una domanda molto interessante, non sono mai stato tentato di utilizzare classi generiche statiche, ma almeno sembra possibile.
Nel contesto della dichiarazione dei metodi di estensione, è possibile non solo dichiarare i metodi di estensione per un determinato tipo generico (ad esempio IEnumerable<T>
) ma inserire anche il parametro di tipo T
nell'equazione. Se accettiamo di trattare IEnumerable<int>
e IEnumerable<string>
come tipi diversi, questo ha senso anche a livello concettuale.
La possibilità di dichiarare i propri metodi di estensione in una classe generica statica consente di risparmiare ripetendo i vincoli dei parametri di tipo più e più volte, raggruppando tutti i metodi di estensione per IEnumerable<T> where T : IComparable
insieme.
In base alle specifiche (citazione necessaria), i metodi di estensione possono essere dichiarati solo in classi statiche non nidificate e non generiche. Il motivo dei primi due vincoli è abbastanza ovvio:
- Non può portare nessuno stato in quanto non è un mixin, solo zucchero sintattico.
- Deve avere lo stesso ambito lessicale del tipo per cui fornisce estensioni.
La restrizione sulle classi statiche non generiche mi sembra un po 'arbitraria, non riesco a trovare una ragione tecnica qui. Ma potrebbe essere che i progettisti linguistici abbiano deciso di scoraggiarti nella scrittura di metodi di estensione che dipendono dal parametro tipo di una classe generica per la quale desideri fornire metodi di estensione. Invece vogliono che forniate un'implementazione veramente generica del vostro metodo di estensione, ma rendono possibile fornire versioni ottimizzate/specializzate (in termini di tempo di compilazione) dei vostri metodi di estensione oltre alla vostra implementazione generale.
Ricordami la specializzazione del modello in C++. EDIT: Purtroppo questo è sbagliato, per favore vedi le mie aggiunte qui sotto.
Ok, poiché questo è un argomento molto interessante ho fatto qualche ulteriore ricerca.In realtà c'è una restrizione tecnica che mi è sfuggita qui. Diamo un'occhiata a qualche codice:
public static class Test
{
public static void DoSomething<T>(this IEnumerable<T> source)
{
Console.WriteLine("general");
}
public static void DoSomething<T>(this IEnumerable<T> source) where T :IMyInterface
{
Console.WriteLine("specific");
}
}
Questo effettivamente riuscire con questo errore di compilazione:
Type 'ConsoleApplication1.Test' already defines a member called 'DoSomething' with the same parameter types
Ok, dopo ci prova a dividerla in due diverse classi estensioni:
public interface IMyInterface { }
public class SomeType : IMyInterface {}
public static class TestSpecific
{
public static void DoSomething<T>(this IEnumerable<T> source) where T : IMyInterface
{
Console.WriteLine("specific");
}
}
public static class TestGeneral
{
public static void DoSomething<T>(this IEnumerable<T> source)
{
Console.WriteLine("general");
}
}
class Program
{
static void Main(string[] args)
{
var general = new List<int>();
var specific = new List<SomeType>();
general.DoSomething();
specific.DoSomething();
Console.ReadLine();
}
}
Contro la mia impressione iniziale (era ieri sera inoltrata), ciò si tradurrebbe in un'ambiguità nei siti di chiamata. Per risolvere questa ambiguità è necessario chiamare il metodo di estensione in modo tradizionale, ma questo è contro la nostra intenzione.
Quindi questo ci lascia una situazione in cui non è possibile dichiarare le specializzazioni generiche legate ai tempi di compilazione dei metodi di estensione. D'altra parte, non c'è ancora alcun motivo per cui non è stato possibile dichiarare i metodi di estensione solo per un singolo parametro di tipo generico speciale. Quindi sarebbe bello dichiararli in una classe generica statica.
D'altra parte la scrittura di un metodo di estensione del tipo:
public static void DoSomething<T>(this IEnumerable<T> source) where T : IMyInterface {}
o
public static void DoSomething(this IEnumerable<IMyInterface> source) {}
non è tutto troppo diverso e richiede solo un po 'di fusione sul sito chiamata contro sull'estensione lato metodo (probabilmente applicherete un'ottimizzazione che dipende dal tipo specifico, quindi dovrete lanciare T su IMyInterface o comunque). Quindi l'unica ragione per cui riesco a venire è, ancora una volta, che i designer linguistici vogliono incoraggiarti a scrivere estensioni generiche solo in un modo veramente generico.
Qui potrebbero accadere alcune cose interessanti se prendiamo la contravarianza nell'equazione, che stanno per essere introdotte con C# 4.0.
@Dan Bryant, sono consentite classi statiche generiche. Solo non per le classi che contengono metodi di estensione. – Dykam
Oltre alla domanda ... Perché nel tuo primo esempio è .ToArray() definito come metodo di estensione? Se è nella classe SimpleLinkedList sicuramente puoi semplicemente definire un metodo pubblico T [] ToArray() lì. –
Probabilmente per semplificare la scrittura di compilatori (non solo il compilatore C#) in quanto riduce il numero di condizioni da controllare. –