Perché non posso avere metodi statici astratti in C #?

Ultimamente ho lavorato con i provider con successo, e mi sono imbattuto in una situazione interessante in cui volevo avere una class astratta che avesse un metodo statico astratto. Ho letto alcuni post sull’argomento, e in qualche modo aveva senso, ma c’è una bella spiegazione chiara?

I metodi statici non sono istanziati in quanto tali, sono solo disponibili senza riferimento a un object.

Una chiamata a un metodo statico viene eseguita attraverso il nome della class, non attraverso un riferimento a un object, e il codice IL per chiamarlo chiamerà il metodo astratto attraverso il nome della class che lo ha definito, non necessariamente il nome della class che hai usato .

Lasciami mostrare un esempio.

Con il seguente codice:

public class A { public static void Test() { } } public class B : A { } 

Se chiami B.Test, in questo modo:

 class Program { static void Main(string[] args) { B.Test(); } } 

Quindi il codice effettivo all’interno del metodo Main è il seguente:

 .entrypoint .maxstack 8 L0000: nop L0001: call void ConsoleApplication1.A::Test() L0006: nop L0007: ret 

Come puoi vedere, la chiamata viene effettuata su A.Test, perché era la class A che l’ha definita e non a B.Test, anche se puoi scrivere il codice in questo modo.

Se avessi dei tipi di class , come in Delphi, in cui puoi creare una variabile che si riferisce a un tipo e non a un object, avresti più utilità per i metodi statici virtuali e quindi astratti (e anche per i costruttori), ma non sono disponibili e quindi le chiamate statiche non sono virtuali in .NET.

Mi rendo conto che i progettisti di IL potrebbero consentire la compilazione del codice per chiamare B.Test e risolvere la chiamata in fase di runtime, ma non sarebbe ancora virtuale, in quanto dovresti comunque scrivere una sorta di nome di class.

I metodi virtuali, e quindi quelli astratti, sono utili solo quando si utilizza una variabile che, in fase di runtime, può contenere molti tipi diversi di oggetti e quindi si desidera chiamare il metodo giusto per l’object corrente che si ha nella variabile. Con i metodi statici è comunque necessario passare attraverso un nome di class, quindi il metodo esatto da chiamare è noto al momento della compilazione perché non può e non cambierà.

Pertanto, i metodi statici virtuali / astratti non sono disponibili in .NET.

I metodi statici non possono essere ereditati o sovrascritti, ed è per questo che non possono essere astratti. Poiché i metodi statici sono definiti sul tipo, non sull’istanza, di una class, devono essere chiamati esplicitamente su quel tipo. Pertanto, quando si desidera chiamare un metodo su una class figlia, è necessario utilizzarne il nome per chiamarlo. Ciò rende l’eredità irrilevante.

Supponiamo di poter, per un momento, ereditare metodi statici. Immagina questo scenario:

 public static class Base { public static virtual int GetNumber() { return 5; } } public static class Child1 : Base { public static override int GetNumber() { return 1; } } public static class Child2 : Base { public static override int GetNumber() { return 2; } } 

Se chiami Base.GetNumber (), quale metodo verrebbe chiamato? Quale valore è stato restituito? È piuttosto facile vedere che senza creare istanze di oggetti, l’ereditarietà è piuttosto difficile. I metodi astratti senza ereditarietà sono solo metodi che non hanno un corpo, quindi non possono essere chiamati.

Un altro intervistato (McDowell) ha detto che il polimorfismo funziona solo per le istanze di oggetti. Questo dovrebbe essere qualificato; ci sono linguaggi che trattano le classi come istanze di un tipo “Class” o “Metaclass”. Questi linguaggi supportano il polimorfismo per entrambi i metodi istanza e class (statici).

C #, come Java e C ++ prima di esso, non è un linguaggio del genere; la parola chiave static viene utilizzata esplicitamente per indicare che il metodo è legato staticamente anziché dinamico / virtuale.

Per aggiungere alle spiegazioni precedenti, le chiamate al metodo statico sono legate a un metodo specifico in fase di compilazione , che invece esclude il comportamento polimorfico.

Ecco una situazione in cui c’è sicuramente bisogno di ereditarietà per campi e metodi statici:

 abstract class Animal { protected static string[] legs; static Animal() { legs=new string[0]; } public static void printLegs() { foreach (string leg in legs) { print(leg); } } } class Human: Animal { static Human() { legs=new string[] {"left leg", "right leg"}; } } class Dog: Animal { static Dog() { legs=new string[] {"left foreleg", "right foreleg", "left hindleg", "right hindleg"}; } } public static void main() { Dog.printLegs(); Human.printLegs(); } //what is the output? //does each subclass get its own copy of the array "legs"? 

In realtà sovrascriviamo i metodi statici (in delphi), è un po ‘brutto, ma funziona perfettamente per i nostri bisogni.

Lo usiamo in modo che le classi possano avere una lista dei loro oggetti disponibili senza l’istanza della class, ad esempio, abbiamo un metodo che assomiglia a questo:

 class function AvailableObjects: string; override; begin Result := 'Object1, Object2'; end; 

È brutto ma necessario, in questo modo possiamo istanziare solo ciò che è necessario, invece di avere tutte le classi istanziate solo per cercare gli oggetti disponibili.

Questo era un semplice esempio, ma l’applicazione stessa è un’applicazione client-server che ha tutte le classi disponibili in un solo server e più client diversi che potrebbero non aver bisogno di tutto ciò che il server ha e non avrà mai bisogno di un’istanza dell’object.

Quindi questo è molto più facile da mantenere rispetto ad avere una diversa applicazione server per ogni client.

Spero che l’esempio sia stato chiaro.

I metodi astratti sono implicitamente virtuali. I metodi astratti richiedono un’istanza, ma i metodi statici non hanno un’istanza. Quindi, puoi avere un metodo statico in una class astratta, ma non può essere astratto statico (o statico astratto).