Pourquoi ne puis-je pas avoir de méthodes statiques abstraites en C #?

182

J'ai travaillé pas mal avec des fournisseurs ces derniers temps, et je suis tombé sur une situation intéressante où je voulais avoir une classe abstraite qui avait une méthode statique abstraite. J'ai lu quelques articles sur le sujet, et cela avait du sens, mais y a-t-il une bonne explication claire?

lomaxx
la source
3
Veuillez les laisser ouvertes pour permettre de futures améliorations.
Mark Biek
6
Je pense que la question est de savoir que C # a besoin d'un autre mot-clé, précisément pour ce genre de situation. Vous voulez une méthode dont la valeur de retour dépend uniquement du type sur lequel elle est appelée. Vous ne pouvez pas l'appeler "statique" si ledit type est inconnu. Mais une fois que le type sera connu, il deviendra statique. "Statique non résolu" est l'idée - ce n'est pas encore statique, mais une fois que nous connaîtrons le type de réception, il le sera. C'est un très bon concept, c'est pourquoi les programmeurs ne cessent de le demander. Mais cela ne correspondait pas tout à fait à la façon dont les concepteurs pensaient du langage.
William Jockusch
@WilliamJockusch que signifie le type de réception? Si j'appelle BaseClass.StaticMethod () alors BaseClass est le seul type qu'il peut utiliser pour prendre la décision. Mais à ce niveau, c'est abstrait, donc la méthode ne peut pas être résolue. Si vous appelez plutôt bien DerivedClass.StaticMethod, la classe de base n'est pas pertinente.
Martin Capodici
Dans la classe de base, la méthode n'est pas résolue et vous ne pouvez pas l'utiliser. Vous avez besoin d'un type dérivé ou d'un objet (qui à son tour aurait un type dérivé). Vous devriez pouvoir appeler baseClassObject.Method () ou DerivedClass.Method (). Vous ne pouvez pas appeler BaseClass.Method () car cela ne vous donne pas le type.
William Jockusch

Réponses:

157

Les méthodes statiques ne sont pas instanciées en tant que telles, elles sont simplement disponibles sans référence d'objet.

Un appel à une méthode statique se fait via le nom de la classe, pas via une référence d'objet, et le code IL (Intermediate Language) pour l'appeler appellera la méthode abstraite via le nom de la classe qui l'a définie, pas nécessairement le nom de la classe que vous avez utilisée.

Laissez-moi vous montrer un exemple.

Avec le code suivant:

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

public class B : A
{
}

Si vous appelez B.Test, comme ceci:

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

Ensuite, le code réel à l'intérieur de la méthode Main est le suivant:

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

Comme vous pouvez le voir, l'appel est fait à A.Test, car c'est la classe A qui l'a défini, et non à B.Test, même si vous pouvez écrire le code de cette façon.

Si vous aviez des types de classe , comme dans Delphi, où vous pouvez créer une variable faisant référence à un type et non à un objet, vous auriez plus d'utilité pour les méthodes statiques virtuelles et donc abstraites (et aussi les constructeurs), mais elles ne sont pas disponibles et ainsi les appels statiques ne sont pas virtuels dans .NET.

Je me rends compte que les concepteurs d'IL pourraient permettre au code d'être compilé pour appeler B.Test, et résoudre l'appel au moment de l'exécution, mais ce ne serait toujours pas virtuel, car vous auriez encore à écrire une sorte de nom de classe là-bas.

Les méthodes virtuelles, et donc abstraites, ne sont utiles que lorsque vous utilisez une variable qui, à l'exécution, peut contenir de nombreux types d'objets différents, et que vous souhaitez donc appeler la bonne méthode pour l'objet actuel que vous avez dans la variable. Avec les méthodes statiques, vous devez de toute façon passer par un nom de classe, donc la méthode exacte à appeler est connue au moment de la compilation car elle ne peut pas et ne changera pas.

Ainsi, les méthodes statiques virtuelles / abstraites ne sont pas disponibles dans .NET.

Lasse V. Karlsen
la source
4
Combiné avec la façon dont la surcharge d'opérateur est effectuée en C #, cela élimine malheureusement la possibilité d'exiger des sous-classes pour fournir une implémentation pour une surcharge d'opérateur donnée.
Chris Moschini
23
Je ne trouve pas cette réponse très utile car la définition de Test()est Aplutôt que abstraite et potentiellement définie dans B. \
5
Les paramètres de type générique se comportent effectivement comme des variables de «type» non persistantes, et les méthodes statiques virtuelles pourraient être utiles dans un tel contexte. Par exemple, si l'on avait un Cartype avec une CreateFromDescriptionméthode de fabrique statique virtuelle , alors le code qui acceptait un Cartype générique -constrained Tpourrait appeler T.CreateFromDescriptionpour produire une voiture de type T. Une telle construction pourrait être assez bien supportée dans le CLR si chaque type qui définissait une telle méthode contenait une instance de singleton statique d'une classe imbriquée générique qui contenait les méthodes «statiques» virtuelles.
supercat
45

Les méthodes statiques ne peuvent pas être héritées ou remplacées, et c'est pourquoi elles ne peuvent pas être abstraites. Comme les méthodes statiques sont définies sur le type, et non sur l'instance, d'une classe, elles doivent être appelées explicitement sur ce type. Ainsi, lorsque vous souhaitez appeler une méthode sur une classe enfant, vous devez utiliser son nom pour l'appeler. Cela rend l'héritage non pertinent.

Supposons que vous puissiez, pendant un moment, hériter des méthodes statiques. Imaginez ce scénario:

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; }
}

Si vous appelez Base.GetNumber (), quelle méthode sera appelée? Quelle valeur renvoyée? Il est assez facile de voir que sans créer d'instances d'objets, l'héritage est plutôt difficile. Les méthodes abstraites sans héritage ne sont que des méthodes qui n'ont pas de corps et ne peuvent donc pas être appelées.

David Wengier
la source
33
Compte tenu de votre scénario, je dirais que Base.GetNumber () renverrait 5; Child1.GetNumber () renvoie 1; Child2.GetNumber () renvoie 2; Pouvez-vous me prouver le contraire, pour m'aider à comprendre votre raisonnement? Merci
Luis Filipe
Le visage que vous pensez que Base.GetNumber () renvoie 5, signifie que vous comprenez déjà ce qui se passe. En renvoyant la valeur de base, il n'y a pas d'héritage en cours.
David Wengier
60
Pourquoi diable Base.GetNumber () renverrait-il autre chose que 5? C'est une méthode dans la classe de base - il n'y a qu'une seule option.
Artem Russakovskii
4
@ArtemRussakovskii: Supposons qu'il y en ait un int DoSomething<T>() where T:Base {return T.GetNumber();}. Il semblerait utile de DoSomething<Base>()pouvoir en renvoyer cinq, tandis DoSomething<Child2>()que d'en renvoyer deux. Une telle capacité serait non seulement utile pour les exemples de jouets, mais aussi pour quelque chose comme class Car {public static virtual Car Build(PurchaseOrder PO);}, où chaque classe dérivant Cardevrait définir une méthode qui pourrait construire une instance à partir d'un bon de commande.
supercat
4
Il y a exactement le même «problème» avec l'héritage non statique.
Ark-kun
18

Un autre répondant (McDowell) a déclaré que le polymorphisme ne fonctionne que pour les instances d'objets. Cela devrait être nuancé; il existe des langages qui traitent les classes comme des instances de type "Class" ou "Metaclass". Ces langages prennent en charge le polymorphisme pour les méthodes d'instance et de classe (statiques).

C #, comme Java et C ++ avant lui, n'est pas un tel langage; le staticmot-clé est utilisé explicitement pour indiquer que la méthode est liée statiquement plutôt que dynamique / virtuelle.

Chris Hanson
la source
9

Voici une situation où il y a certainement un besoin d'héritage pour les champs et méthodes statiques:

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"?
user275801
la source
4
Non, il n'y a qu'une seule instance du tableau «jambes». La sortie n'est pas déterministe car vous ne savez pas dans quel ordre les constructeurs statiques seront appelés (il n'y a en fait aucune garantie que le constructeur statique de la classe de base sera appelé du tout). «Besoin» est un terme assez absolu où «désir» est probablement plus précis.
Sam
legsdoit être une propriété abstraite statique.
Little Endian
8

Pour ajouter aux explications précédentes, les appels de méthodes statiques sont liés à une méthode spécifique au moment de la compilation , ce qui exclut plutôt un comportement polymorphe.

Rytmis
la source
C # est typé statiquement; les appels aux méthodes polymorphes sont également liés au moment de la compilation si je comprends bien - c'est-à-dire que le CLR n'est pas laissé pour résoudre la méthode à appeler pendant l'exécution.
Adam Tolley
Alors, comment pensez-vous que le polymorphisme fonctionne exactement sur le CLR? Votre explication a simplement exclu l'envoi de méthode virtuelle.
Rytmis
Ce n'est pas vraiment un commentaire aussi utile qu'il pourrait l'être. J'ai invité (avec «si je comprends bien») un discours utile, je pense que vous pourriez peut-être fournir un peu plus de contenu - vu que les gens viennent ici chercher des réponses et non des insultes. Bien qu'il semble que je puisse être coupable de la même chose - je voulais vraiment dire le commentaire ci-dessus comme une question: C # n'évalue-t-il pas ces choses au moment de la compilation?
Adam Tolley
Toutes mes excuses, je ne voulais pas insulter (même si j'avoue avoir répondu un peu vite ;-). Le point de ma question était, si vous avez ces classes: class Base {public virtual void Method (); } classe Derived: Base {public override void Method (); } et écrivez ainsi: Base instance = new Derived (); instance.Method (); les informations de type au moment de la compilation sur le site d'appel indiquent que nous avons une instance de Base, lorsque l'instance réelle est un Derived. Le compilateur ne peut donc pas résoudre la méthode exacte à appeler. Au lieu de cela, il émet une instruction IL "callvirt" qui indique à l'exécution de distribuer ..
Rytmis
1
Merci mec, c'est instructif! Je suppose que je repousse assez longtemps la plongée en IL, je souhaite bonne chance.
Adam Tolley
5

Nous remplaçons en fait les méthodes statiques (en delphi), c'est un peu moche, mais cela fonctionne très bien pour nos besoins.

Nous l'utilisons pour que les classes puissent avoir une liste de leurs objets disponibles sans l'instance de classe, par exemple, nous avons une méthode qui ressemble à ceci:

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

C'est moche mais nécessaire, de cette façon nous pouvons instancier juste ce qui est nécessaire, au lieu d'avoir toutes les classes instanciées juste pour rechercher les objets disponibles.

C'était un exemple simple, mais l'application elle-même est une application client-serveur qui a toutes les classes disponibles sur un seul serveur, et plusieurs clients différents qui pourraient ne pas avoir besoin de tout ce que le serveur a et n'auront jamais besoin d'une instance d'objet.

C'est donc beaucoup plus facile à gérer que d'avoir une application serveur différente pour chaque client.

J'espère que l'exemple était clair.

Fabio Gomes
la source
0

Les méthodes abstraites sont implicitement virtuelles. Les méthodes abstraites nécessitent une instance, mais les méthodes statiques n'ont pas d'instance. Ainsi, vous pouvez avoir une méthode statique dans une classe abstraite, elle ne peut tout simplement pas être statique abstraite (ou statique abstraite).

AMing
la source
1
-1 Les méthodes virtuelles n'ont pas besoin d'instance, sauf de par leur conception. Et vous ne répondez pas vraiment à la question, mais vous la détournez.
FallenAvatar