Pourquoi ne puis-je pas hériter de classes statiques?

224

J'ai plusieurs classes qui n'ont pas vraiment besoin d'un état. Du point de vue organisationnel, je voudrais les hiérarchiser.

Mais il semble que je ne puisse pas déclarer d'héritage pour les classes statiques.

Quelque chose comme ca:

public static class Base
{
}

public static class Inherited : Base
{
}

ne fonctionnera pas.

Pourquoi les concepteurs de la langue ont-ils fermé cette possibilité?

Utilisateur
la source

Réponses:

174

Citation d' ici :

C'est en fait par conception. Il ne semble pas y avoir de bonne raison d'hériter d'une classe statique. Il a des membres statiques publics auxquels vous pouvez toujours accéder via le nom de classe lui-même. Les seules raisons pour lesquelles j'ai hérité des éléments statiques sont de mauvaises, telles que l'enregistrement de quelques caractères de frappe.

Il peut y avoir des raisons d'envisager des mécanismes pour amener les membres statiques directement dans le champ d'application (et nous le considérerons en fait après le cycle du produit Orcas), mais l'héritage de classe statique n'est pas la voie à suivre: c'est le mauvais mécanisme à utiliser et fonctionne uniquement pour les membres statiques résidant dans une classe statique.

(Mads Torgersen, PM en langage C #)

Autres opinions de channel9

L'héritage dans .NET fonctionne uniquement sur la base d'instance. Les méthodes statiques sont définies au niveau du type et non au niveau de l'instance. C'est pourquoi le remplacement ne fonctionne pas avec les méthodes / propriétés / événements statiques ...

Les méthodes statiques ne sont conservées qu'une seule fois en mémoire. Il n'y a pas de table virtuelle, etc. créée pour eux.

Si vous appelez une méthode d'instance dans .NET, vous lui donnez toujours l'instance actuelle. Ceci est masqué par le runtime .NET, mais cela se produit. Chaque méthode d'instance a comme premier argument un pointeur (référence) vers l'objet sur lequel la méthode est exécutée. Cela ne se produit pas avec les méthodes statiques (car elles sont définies au niveau du type). Comment le compilateur doit-il décider de sélectionner la méthode à invoquer?

(petit gourou)

Et comme idée valable, littleguru a une "solution de contournement" partielle pour ce problème: le modèle Singleton .

boj
la source
92
Manque d'imagination du côté de Torgersen, vraiment. J'avais une assez bonne raison de vouloir faire ça :)
Thorarin
10
Que dis-tu de ça? Vous avez une DLL qui n'est pas open source / vous n'avez pas accès à sa source, disons NUnit. Vous voulez étendre sa classe Assert pour avoir une méthode comme Throws <> (Action a) (oui, c'est là aussi, mais à un moment donné, ce n'était pas le cas.) Vous pouvez ajouter à votre projet un Assert: NUnit.Framework.Assert , puis ajoutez une méthode Throws. Problème résolu. C'est plus propre à utiliser dans le code aussi ... au lieu d'avoir à trouver un autre nom de classe et de vous souvenir de ce nom de classe, tapez simplement Assert. et voir les méthodes disponibles.
user420667
4
@KonradMorawski Il est impossible d'écrire des méthodes d'extension pour les classes statiques. stackoverflow.com/questions/249222/…
Martin Braun
2
@modiX Bien sûr. Vous m'avez mal compris ici. Ce que je voulais dire, c'est que la fonctionnalité requise dans le scénario décrit par user420667 pourrait être fournie ( à l'avenir ) aussi peu que permettre des méthodes d'extension pour les classes statiques. Aucun héritage statique nécessaire. C'est pourquoi son scénario ne m'a pas semblé être une très bonne raison pour introduire l'héritage statique. Si C # devait être modifié dans cet aspect, pourquoi opter pour une refonte majeure alors qu'une mineure serait tout aussi bonne en pratique.
Konrad Morawski
5
@Learner Ce n'est pas vraiment juste. Dans .Net, tout hérite objectet tout le monde est censé le savoir. Les classes statiques héritent donc toujours de object, que vous le spécifiiez explicitement ou non.
RenniePet
71

La principale raison pour laquelle vous ne pouvez pas hériter d'une classe statique est qu'elles sont abstraites et scellées (cela empêche également leur création).

Donc ça:

static class Foo { }

compile dans cet IL:

.class private abstract auto ansi sealed beforefieldinit Foo
  extends [mscorlib]System.Object
 {
 }
Andrew Hare
la source
17
Vous dites que statique a été implémenté comme abstrait + scellé. Il veut savoir pourquoi cela a été fait. Pourquoi est-il scellé?
Lucas
22
Cela ne répond pas tout à la question; cela ne fait que reformuler la question sur laquelle il posait des questions.
Igby Largeman
28
Eh bien, l'OP aurait dû demander "Pourquoi les classes statiques sont-elles scellées", et non "Pourquoi ne puis-je pas hériter des classes statiques?", La réponse est bien sûr "parce qu'elles sont scellées" . Cette réponse obtient mon vote.
Alex Budovski
6
merci d'avoir partagé que les classes statiques sont automatiquement compilées en tant que classes scellées - je me demandais comment / quand cela s'est produit!
Mark
23
@AlexBudovski: Cette réponse est correcte mais aussi inutile que de dire à une personne perdue "vous êtes devant moi" quand elle vous demande "où suis-je".
DeepSpace101
24

Pensez-y de cette façon: vous accédez aux membres statiques via le nom du type, comme ceci:

MyStaticType.MyStaticMember();

Si vous héritiez de cette classe, vous devriez y accéder via le nouveau nom de type:

MyNewType.MyStaticMember();

Ainsi, le nouvel élément n'a aucun rapport avec l'original lorsqu'il est utilisé dans le code. Il n'y aurait aucun moyen de profiter d'une relation d'héritage pour des choses comme le polymorphisme.

Vous pensez peut-être que vous souhaitez simplement étendre certains des éléments de la classe d'origine. Dans ce cas, rien ne vous empêche d'utiliser simplement un membre de l'original dans un type entièrement nouveau.

Vous souhaitez peut-être ajouter des méthodes à un type statique existant. Vous pouvez déjà le faire via des méthodes d'extension.

Peut-être voulez-vous pouvoir passer un statique Typeà une fonction au moment de l'exécution et appeler une méthode sur ce type, sans savoir exactement ce que fait la méthode. Dans ce cas, vous pouvez utiliser une interface.

Donc, à la fin, vous ne gagnez vraiment rien à hériter des classes statiques.

Joel Coehoorn
la source
5
Vous ne pouvez pas ajouter de méthodes à un type statique existant via une méthode d'extension. Veuillez voir mon commentaire sur la réponse acceptée pour un exemple d'utilisation souhaitée. (Fondamentalement, vous voudriez simplement renommer ce que vous avez nommé MyNewType en MyStaticType, donc MyStaticType: OldProject.MyStaticType)
user420667
2
On pourrait définir des relations d'héritage avec des types statiques et des membres statiques virtuels de telle manière que l'on SomeClass<T> where T:Foopourrait accéder aux membres de Foo, et s'il Ts'agissait d'une classe qui remplaçait certains membres statiques virtuels de Foo, la classe générique utiliserait ces remplacements. Il serait probablement même possible de définir une convention par laquelle les langues pourraient le faire d'une manière compatible avec le CLR actuel (par exemple, une classe avec de tels membres devrait définir une classe non statique protégée qui contient de tels membres d'instance, avec un champ statique contenant une instance de ce type).
supercat
Je me suis retrouvé avec un cas où je pense que hériter d'une classe statique est la bonne chose à faire, bien que vous puissiez évidemment y accéder de toute façon. J'ai une classe statique pour envoyer des flux de données brutes à l'imprimante (pour parler aux imprimantes d'étiquettes industrielles). Il a un tas de déclarations API Windows. Je me retrouve maintenant à écrire une autre classe pour jouer avec des imprimantes qui ont besoin des mêmes appels API. Combiner? Pas bon. Les déclarer deux fois? Laid. Hériter? Bon, mais pas autorisé.
Loren Pechtel
3

Ce que vous voulez réaliser en utilisant la hiérarchie des classes peut être réalisé simplement par le biais de l'espace de noms. Ainsi, les langages qui prennent en charge les espaces de noms (comme C #) n'auront aucune utilité d'implémenter la hiérarchie des classes de classes statiques. Comme vous ne pouvez instancier aucune des classes, tout ce dont vous avez besoin est une organisation hiérarchique des définitions de classe que vous pouvez obtenir en utilisant des espaces de noms

Amit
la source
J'ai un chargeur générique qui reçoit une classe comme type. Cette classe a 5 méthodes d'assistance et 2 méthodes qui renvoient une chaîne (nom et description). J'ai peut-être mal choisi (je pense que oui), mais la seule solution que j'ai trouvée a été d'instancier la classe dans le chargeur générique, d'accéder aux méthodes ... meh.
ANeves
L'alternative serait un dictionnaire géant, je suppose. De plus, lorsque vous dites «ce que vous voulez réaliser», vous devriez plutôt dire «ce que vous semblez viser» ou quelque chose de similaire.
ANeves
3

Vous pouvez utiliser la composition à la place ... cela vous permettra d'accéder aux objets de classe à partir du type statique. Mais je ne peux toujours pas implémenter des interfaces ou des classes abstraites

Yogesh Karekar
la source
2

Bien que vous puissiez accéder aux membres statiques "hérités" via le nom des classes héritées, les membres statiques ne sont pas vraiment hérités. C'est en partie pourquoi ils ne peuvent pas être virtuels ou abstraits et ne peuvent pas être remplacés. Dans votre exemple, si vous avez déclaré un Base.Method (), le compilateur mappera de toute façon un appel à Inherited.Method () à Base.Method (). Vous pourriez aussi bien appeler explicitement Base.Method (). Vous pouvez écrire un petit test et voir le résultat avec Reflector.

Donc ... si vous ne pouvez pas hériter de membres statiques, et si les classes statiques ne peuvent contenir que des membres statiques, à quoi bon hériter d'une classe statique?

Lucas
la source
1

Hmmm ... serait-ce bien différent si vous aviez juste des classes non statiques remplies de méthodes statiques ..?

CookieOfFortune
la source
2
C'est ce qui me reste. Je le fais de cette façon. Et je n'aime pas ça.
Utilisateur
Je l'ai également fait de cette façon, mais pour une raison quelconque, cela ne semble pas esthétique lorsque vous savez que vous n'instancierez jamais l'objet. Je suis toujours angoissé par des détails comme celui-ci, malgré le fait qu'il n'y ait aucun coût matériel.
gdbj
Dans une classe non statique remplie de méthodes statiques, vous ne pouvez pas mettre de méthodes d'extension :)
Jakub Szułakiewicz
1

Une solution de contournement que vous pouvez faire n'est pas d'utiliser des classes statiques mais de masquer le constructeur afin que les membres statiques des classes soient la seule chose accessible en dehors de la classe. Le résultat est une classe "statique" héritable essentiellement:

public class TestClass<T>
{
    protected TestClass()
    { }

    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public class TestClass : TestClass<double>
{
    // Inherited classes will also need to have protected constructors to prevent people from creating instances of them.
    protected TestClass()
    { }
}

TestClass.Add(3.0, 4.0)
TestClass<int>.Add(3, 4)

// Creating a class instance is not allowed because the constructors are inaccessible.
// new TestClass();
// new TestClass<int>();

Malheureusement, en raison de la limitation du langage "par conception", nous ne pouvons pas faire:

public static class TestClass<T>
{
    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public static class TestClass : TestClass<double>
{
}
FocusedWolf
la source
1

Vous pouvez faire quelque chose qui ressemblera à un héritage statique.

Voici l'astuce:

public abstract class StaticBase<TSuccessor>
    where TSuccessor : StaticBase<TSuccessor>, new()
{
    protected static readonly TSuccessor Instance = new TSuccessor();
}

Ensuite, vous pouvez le faire:

public class Base : StaticBase<Base>
{
    public Base()
    {
    }

    public void MethodA()
    {
    }
}

public class Inherited : Base
{
    private Inherited()
    {
    }

    public new static void MethodA()
    {
        Instance.MethodA();
    }
}

La Inheritedclasse n'est pas statique elle-même, mais nous ne permettons pas de la créer. Il a en fait hérité du constructeur statique qui construit Baseet de toutes les propriétés et méthodes Basedisponibles en tant que statique. Maintenant, il ne reste plus qu'à créer des wrappers statiques pour chaque méthode et propriété que vous devez exposer à votre contexte statique.

Il y a des inconvénients comme la nécessité de créer manuellement des méthodes et des newmots clés d' encapsuleurs statiques . Mais cette approche permet de prendre en charge quelque chose qui est vraiment similaire à l'héritage statique.

PS Nous l'avons utilisé pour créer des requêtes compilées, et cela peut en fait être remplacé par ConcurrentDictionary, mais un champ statique en lecture seule avec sa sécurité de thread était assez bon.

Konard
la source
1

Ma réponse: mauvais choix de conception. ;-)

Il s'agit d'un débat intéressant axé sur l'impact syntaxique. À mon avis, l'essentiel de l'argument est qu'une décision de conception a conduit à des classes statiques scellées. Un accent sur la transparence des noms de classe statiques apparaissant au niveau supérieur au lieu de se cacher («confus») derrière les noms d'enfants? On peut imaginer une implémentation de langage qui pourrait accéder directement à la base ou à l'enfant, déroutant.

Un pseudo exemple, en supposant que l'héritage statique a été défini d'une manière ou d'une autre.

public static class MyStaticBase
{
    SomeType AttributeBase;
}

public static class MyStaticChild : MyStaticBase
{
    SomeType AttributeChild;
}

conduirait à:

 // ...
 DoSomethingTo(MyStaticBase.AttributeBase);
// ...

qui pourrait (aurait?) un impact sur le même stockage que

// ...
DoSomethingTo(MyStaticChild.AttributeBase);
// ...

Très perturbant!

Mais attendez! Comment le compilateur gérerait-il MyStaticBase et MyStaticChild ayant la même signature définie dans les deux? Si l'enfant remplace ce que mon exemple ci-dessus ne changerait PAS le même stockage, peut-être? Cela conduit à encore plus de confusion.

Je crois qu'il existe une forte justification de l'espace informationnel pour un héritage statique limité. Plus sur les limites sous peu. Ce pseudocode montre la valeur:

public static class MyStaticBase<T>
{
   public static T Payload;
   public static void Load(StorageSpecs);
   public static void Save(StorageSpecs);
   public static SomeType AttributeBase
   public static SomeType MethodBase(){/*...*/};
}

Vous obtenez alors:

public static class MyStaticChild : MyStaticBase<MyChildPlayloadType>
{
   public static SomeType AttributeChild;
   public static SomeType SomeChildMethod(){/*...*/};
   // No need to create the PlayLoad, Load(), and Save().
   // You, 'should' be prevented from creating them, more on this in a sec...
} 

L'utilisation ressemble à:

// ...
MyStaticChild.Load(FileNamePath);
MyStaticChild.Save(FileNamePath);
doSomeThing(MyStaticChild.Payload.Attribute);
doSomething(MyStaticChild.AttributeBase);
doSomeThing(MyStaticChild.AttributeChild);
// ...

La personne qui crée l'enfant statique n'a pas besoin de penser au processus de sérialisation tant qu'elle comprend les limitations qui pourraient être placées sur le moteur de sérialisation de la plateforme ou de l'environnement.

La statique (singletons et autres formes de «globaux») survient souvent autour du stockage de la configuration. L'héritage statique permettrait à ce type d'allocation de responsabilité d'être clairement représenté dans la syntaxe pour correspondre à une hiérarchie de configurations. Bien que, comme je l'ai montré, il existe de nombreuses possibilités d'ambiguïté massive si des concepts de base d'héritage statique sont mis en œuvre.

Je crois que le bon choix de conception serait de permettre l'héritage statique avec des limitations spécifiques:

  1. Aucune dérogation à quoi que ce soit. L'enfant ne peut pas remplacer les attributs de base, les champs ou les méthodes, ... La surcharge devrait être correcte, tant qu'il y a une différence de signature permettant au compilateur de trier l'enfant par rapport à la base.
  2. Autoriser uniquement les bases statiques génériques, vous ne pouvez pas hériter d'une base statique non générique.

Vous pouvez toujours changer le même magasin via une référence générique MyStaticBase<ChildPayload>.SomeBaseField. Mais vous seriez découragé car le type générique devrait être spécifié. Alors que la référence de l' enfant serait plus propre: MyStaticChild.SomeBaseField.

Je ne suis pas un écrivain de compilateur, donc je ne sais pas si je manque quelque chose sur les difficultés de mise en œuvre de ces limitations dans un compilateur. Cela dit, je crois fermement qu'il existe un besoin d'espace informationnel pour un héritage statique limité et la réponse de base est que vous ne pouvez pas en raison d'un choix de conception médiocre (ou trop simple).

Dale Strickler
la source
0

Les classes statiques et les membres de classe sont utilisés pour créer des données et des fonctions accessibles sans créer d'instance de la classe. Les membres de classe statiques peuvent être utilisés pour séparer les données et les comportements qui sont indépendants de toute identité d'objet: les données et les fonctions ne changent pas quoi qu'il arrive à l'objet. Les classes statiques peuvent être utilisées lorsqu'il n'y a pas de données ou de comportement dans la classe qui dépend de l'identité de l'objet.

Une classe peut être déclarée statique, ce qui indique qu'elle ne contient que des membres statiques. Il n'est pas possible d'utiliser le nouveau mot clé pour créer des instances d'une classe statique. Les classes statiques sont chargées automatiquement par le Common Language Runtime (CLR) .NET Framework lorsque le programme ou l'espace de noms qui contient la classe est chargé.

Utilisez une classe statique pour contenir des méthodes qui ne sont pas associées à un objet particulier. Par exemple, il est courant de créer un ensemble de méthodes qui n'agissent pas sur les données d'instance et ne sont pas associées à un objet spécifique dans votre code. Vous pouvez utiliser une classe statique pour conserver ces méthodes.

Voici les principales caractéristiques d'une classe statique:

  1. Ils ne contiennent que des membres statiques.

  2. Ils ne peuvent pas être instanciés.

  3. Ils sont scellés.

  4. Ils ne peuvent pas contenir de constructeurs d'instance (Guide de programmation C #).

La création d'une classe statique est donc fondamentalement la même chose que la création d'une classe qui ne contient que des membres statiques et un constructeur privé. Un constructeur privé empêche la classe d'être instanciée.

L'avantage d'utiliser une classe statique est que le compilateur peut vérifier pour s'assurer qu'aucun membre d'instance n'est ajouté accidentellement. Le compilateur garantit que les instances de cette classe ne peuvent pas être créées.

Les classes statiques sont scellées et ne peuvent donc pas être héritées. Ils ne peuvent hériter d'aucune classe sauf Object. Les classes statiques ne peuvent pas contenir de constructeur d'instance; cependant, ils peuvent avoir un constructeur statique. Pour plus d'informations, consultez Constructeurs statiques (Guide de programmation C #).

Zone
la source
-1

Lorsque nous créons une classe statique qui ne contient que les membres statiques et un constructeur privé. La seule raison est que le constructeur statique empêche la classe d'être instanciée pour cela, nous ne pouvons pas hériter d'une classe statique. La seule façon d'accéder au membre du classe statique en utilisant le nom de classe lui-même.Essayer d'hériter d'une classe statique n'est pas une bonne idée.

Soumen Chakraborty
la source