web-dev-qa-db-fra.com

Membre statique C # "héritage" - pourquoi existe-t-il?

En C #, les membres statiques d'une superclasse sont "hérités" dans la portée des sous-classes. Par exemple:

class A { public static int M() { return 1; } }
class B : A {}
class C : A { public new static int M() { return 2; } }
[...]
A.M(); //returns 1
B.M(); //returns 1 - this is equivalent to A.M()
C.M(); //returns 2 - this is not equivalent to A.M()

Maintenant, vous ne pouvez pas hériter de classes statiques, et le seul endroit où je peux imaginer que l’héritage statique puisse avoir une importance l’ignore complètement: bien que vous puissiez créer une contrainte générique qui requiert un paramètre de type T comme sous-classe de A, T.M() (ce qui simplifie probablement les choses pour la machine virtuelle), écrivez une nouvelle implémentation M dans une sous-classe et utilisez-la.

Ainsi, "l'héritage" des membres statiques ressemble simplement à de la pollution par l'espace de noms; même si vous qualifiez explicitement le nom (c'est-à-dire B.M), la version de A est toujours résolue.

Editcompare avec les espaces de noms:

namespace N1{  class X();   }
namespace N1.N2 {  class X();   }
namespace N1.N2.N3 { [...] }

Dans N1.N2.N3 Il est logique que si j'utilise X sans qualification, il se réfère à N1.N2.X. Mais si je me réfère explicitement à N1.N2.N3.X - et qu'une telle classe n'existe pas - je ne m'attends pas à ce qu'elle trouve la version de N2; et en effet au compilateur rapporte une erreur si vous essayez ceci. En revanche, si je fais explicitement référence à B.M(), pourquoi le compilateur ne signale-t-il pas une erreur? Après tout, il n’ya pas de méthode "M" dans "B" ...

Quel est le but de cet héritage? Cette fonctionnalité peut-elle être utilisée de manière constructive?

43
Eamon Nerbonne

Donc, "l'héritage" de static les membres ressemblent simplement à un espace de noms la pollution

C'est vrai, sauf que la pollution d'un gars est l'arôme épicé ajouté d'un autre.

Je pense que Martin Fowler, dans ses travaux sur les DSL, a suggéré d'utiliser l'héritage de cette manière pour permettre un accès pratique à des méthodes statiques, permettant à ces méthodes d'être utilisées sans qualification de nom de classe. Donc, le code appelant doit être dans une classe qui hérite de la classe dans laquelle les méthodes sont définies. (Je pense que c'est une idée pourrie.)

À mon avis, les membres statiques ne doivent pas être mélangés dans une classe ayant un but non statique, et le problème que vous soulevez est en partie la raison pour laquelle il est important de ne pas les mélanger.

Cacher des données statiques privées mutable dans l’implémentation d’une classe par ailleurs "instancey" est particulièrement horrible. Mais ensuite, il existe des méthodes statiques, qui sont encore pire mélangeurs. Voici une utilisation typique des méthodes statiques mélangées dans une classe:

public class Thing
{
    // typical per-instance stuff
    int _member1;
    protected virtual void Foo() { ... }
    public void Bar() { ... }

    // factory method
    public static Thing Make()
    {
        return new Thing();
    }
}

C'est le modèle de méthode d'usine statique. C'est inutile la plupart du temps, mais le pire est que nous avons maintenant ceci:

public class AnotherThing : Thing { }

Cela a maintenant une méthode Make statique qui retourne une Thing, pas une AnotherThing.

Ce type de décalage implique fortement que tout ce qui a des méthodes statiques doit être scellé. Les membres statiques ne parviennent pas à bien s'intégrer à l'héritage. Cela n'a aucun sens de les avoir héréditaires. Donc, je garde les choses statiques dans des classes statiques séparées, et je crains de devoir redondant de déclarer chaque membre statique alors que j'ai déjà dit que la classe est statique.

Mais c'est juste une de ces choses trop tardives maintenant. Toutes les langues de travail réelles (et les bibliothèques et produits) en ont quelques-unes. C # a remarquablement peu.

24
Daniel Earwicker

J'ai plutôt accès à tous mes membres statiques de base dans les classes dérivées ..__ Sinon, j'aurais besoin de savoir exactement où le membre statique a été défini et de l'appeler explicitement.

Lorsque vous utilisez Intellisense, vous pouvez automatiquement connaître tous les membres statiques disponibles pour ce type de classe.

Bien sûr, ils ne sont pas hérités, c'est juste un raccourci

10
Luis Filipe

Voilà comment cela fonctionne , serait probablement juste une réponse stupide dans la plupart des cas. Mais dans ce cas, c'est comme ça que ça marche. puisque vous dérivez de A, vous dites que vous êtes A + les fonctionnalités supplémentaires que vous ajoutez.

Par conséquent, vous devez pouvoir accéder aux mêmes variables que si vous utilisiez une instance de A.

Cependant, hériter d'une classe statique n'a aucun sens, contrairement à l'accès aux membres/champs/méthodes statiques.

Un exemple de ceci est le suivant:

internal class BaseUser
{
    public static string DefaultUserPool { get; set; }
}
internal class User : BaseUser
{
    public int Id { get; set; }
    public string Name { get; set; }
    public User Parent { get; set; }
}

Où le test ressemble à ceci:

User.DefaultUserPool = "Test";
BaseUser.DefaultUserPool = "Second Test";

Console.WriteLine(User.DefaultUserPool);
Console.WriteLine(BaseUser.DefaultUserPool);

Les deux sorties WriteLines "Second Test", parce que BaseUser et User doivent utiliser DefaultUserPool, de par leur conception . Et remplacer les méthodes statiques implémentées n'aurait pas de sens, puisqu'il s'agit simplement d'un accesseur de la classe enfant.

Il ne peut y en avoir qu'un. Remplacer cela signifierait qu'il y a une nouvelle implémentation pour cette sous-classe, ce qui tuerait le terme "statique".

8
Filip Ekberg

En fait, si je comprends bien, il ne s’agit que d’un raccourci fourni par le compilateur. Sucre de syntaxe. B.M() compilera simplement vers A.M() puisque B n’a pas de static M() et A en a. C'est pour faciliter l'écriture, rien d'autre. Il n'y a pas "d'héritage statique".

Ajouté: Et l'exigence de new lors de la "redéfinition" consiste simplement à ne pas se tirer une balle dans le pied par accident.

5
Vilx-

Je pense que c'est pour accéder aux membres statiques protégés de la classe de base.

class Base
{
    protected static void Helper(string s)
    {
       Console.WriteLine(s);
    }
}

class Subclass : Base
{
   public void Run()
    {
       Helper("From the subclass");
    }
}
0
TrueWill

Alors ... Quelle est l'alternative?

La question mentionne ...

pourquoi le compilateur ne rapporte-t-il pas une erreur? Après tout, il n’ya pas de méthode "M" dans "B" ...

Mais il est une méthode dérivée "M" dans "B" classe.

Si le compilateur ne présente pas au programmeur une table virtuelle unifiée pour les cas de base, il devra alors parcourir les types de base pour rechercher des méthodes statiques. Cela briserait polymorphisme .

Wikipédia...

Le polymorphisme de sous-type, presque universellement appelé simplement polymorphisme dans le contexte de la programmation orientée objet, est la capacité d'un type, A, d'apparaître et d'être utilisé comme un autre type, B ....

Dans les langages fortement typés, le polymorphisme signifie généralement que le type A dérive du type B, ou que le type C implémente une interface qui représente le type B.

0
kervin

Je le vois toujours comme un moyen d'empêcher toute forme de polymorphisme par la classe héritante sur les éléments pour lesquels vous souhaitez conserver la même fonction pour toutes les classes enfants.

ignorer ce qui précède pour une raison quelconque Je pensais à scellé au lieu de statique

Je suppose que vous utiliseriez des variables et des fonctions membres statiques afin de vous assurer que toute donnée ou fonctionnalité ne dépende pas d'une instance de classe, car elle ne serait instanciée qu'une seule fois.

Un exemple d'utilisation serait, par exemple, une valeur de compteur qui garderait un compte réel de toutes les instances des sous-classes d'une superclasse (chaque sous-classe incrémente la valeur du compte statique lors de la construction). Cette valeur de comptage serait disponible et égale pour toutes les instances de la sous-classe.

0
ChrisBD