web-dev-qa-db-fra.com

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

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 l'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 du langage ont-ils fermé cette possibilité?

212
User

Citation de ici :

C'est en fait par conception. Il semble n'y avoir aucune 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 que j'ai vu pour hériter des éléments statiques sont les mauvaises, telles que l'enregistrement de quelques caractères de frappe.

Il y a peut-être lieu d'envisager des mécanismes pour amener les membres statiques directement dans la portée (et nous allons en fait en tenir compte 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 qui résident dans une classe statique.

(Mads Torgersen, langage C # PM)

Autres avis de canal9

L'héritage dans .NET ne fonctionne que sur la base d'instance. Les méthodes statiques sont définies au niveau du type, pas au niveau de l'instance. C'est pourquoi la substitution 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. Aucune table virtuelle, etc. n'a été créée pour eux.

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

(petit gourou)

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

167
boj

La raison principale pour laquelle vous ne pouvez pas hériter d'une classe statique est qu'elle est abstraite et scellée (cela empêche également la création de toute instance de celle-ci).

Donc ça:

static class Foo { }

compile à cette IL:

.class private abstract auto ansi sealed beforefieldinit Foo
  extends [mscorlib]System.Object
 {
 }
70
Andrew Hare

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

MyStaticType.MyStaticMember();

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

MyNewType.MyStaticMember();

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

Peut-être pensez-vous 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.

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

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

Donc, au final, vous n'obtenez vraiment rien en héritant des classes statiques.

23
Joel Coehoorn

Ce que vous voulez réaliser en utilisant la hiérarchie de classes ne peut être obtenu qu’avec un espacement de nom. Ainsi, les langages qui prennent en charge les espaces de noms (comme C #) ne pourront pas utiliser la hiérarchie de classes statiques. Etant donné que vous ne pouvez instancier aucune des classes, vous avez simplement besoin d’une organisation hiérarchique des définitions de classe que vous pouvez obtenir grâce à l’utilisation d’espaces de noms.

3
Amit

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

3
Yogesh Karekar

Bien que vous puissiez accéder aux membres statiques "hérités" via le nom de classes héritées, les membres statiques ne sont pas réellement 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é Base.Method (), le compilateur mappera quand même un appel à Inherited.Method () vers Base.Method (). Vous pouvez aussi appeler Base.Method () explicitement. 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 peuvent contenir niquement membres statiques, à quoi servirait l'héritage d'une classe statique?

2
Lucas

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

1
CookieOfFortune

Une solution de contournement que vous pouvez utiliser consiste à ne pas utiliser de classes statiques mais à 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, à cause de la limitation du langage "par la 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>
{
}
1
FocusedWolf

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

Voici le truc:

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

Ensuite, vous pouvez faire ceci:

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

    public void MethodA()
    {
    }
}

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

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

La classe Inherited n'est pas statique en soi, mais nous ne permettons pas de la créer. Il a en fait hérité du constructeur statique qui construit Base et de toutes les propriétés et méthodes de Base disponibles sous forme statique. Il ne reste plus maintenant qu’à créer des wrappers statiques pour chaque méthode et propriété que vous devez exposer à votre contexte statique.

Il existe des inconvénients tels que la nécessité de créer manuellement des méthodes d'encapsulation statiques et le mot clé new. Mais cette approche permet de prendre en charge quelque chose de très similaire à l'héritage statique.

P.S. Nous avons utilisé cela pour créer des requêtes compilées, et cela peut en réalité être remplacé par ConcurrentDictionary, mais un champ statique en lecture seule avec la sécurité de son thread était suffisant.

1
Konard

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

C'est un débat intéressant axé sur l'impact de la syntaxe. À mon avis, l'essentiel de l'argument est qu'une décision de conception conduit à des classes statiques scellées. Un accent sur la transparence des noms de la classe statique apparaissant au niveau supérieur au lieu de se cacher ("déroutant") derrière les noms d'enfants? On peut imaginer une implémentation de langage pouvant accéder directement à la base ou à l'enfant, ce qui peut prêter à confusion.

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?) avoir un impact sur le même stockage que

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

Très perturbant!

Mais attendez! Comment le compilateur traiterait-il avec MyStaticBase et MyStaticChild ayant la même signature définie dans les deux? Si l'enfant remplace, 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 solide justification de l’espace informationnel pour un héritage statique limité. Plus sur les limites sous peu. Ce pseudocode indique 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(){/*...*/};
}

Ensuite, vous obtenez:

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 réfléchir au processus de sérialisation tant qu'elle comprend les éventuelles limites imposées au moteur de sérialisation de la plate-forme ou de l'environnement.

Les statiques (singletons et autres formes de "globals") apparaissent souvent autour du stockage de 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 un fort potentiel d’ambiguïté massive si les concepts de base d’héritage statique sont mis en œuvre.

Je pense que le bon choix de conception serait de permettre un héritage statique avec des limitations spécifiques:

  1. Aucune dérogation de rien. L'enfant ne peut pas remplacer les attributs de base, les champs ou les méthodes, ... La surcharge devrait être acceptable, tant qu'il existe une différence de signature permettant au compilateur de trier enfant/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é. Bien que la référence enfant soit plus propre: MyStaticChild.SomeBaseField.

Je ne suis pas un rédacteur de compilateur, donc je ne suis pas sûr d'avoir oublié quelque chose sur les difficultés liées à la 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 le faire à cause d'un choix de conception médiocre (ou trop simple).

0
Dale Strickler

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 des données et des comportements indépendants de toute identité d'objet: les données et les fonctions ne changent pas, quel que soit le sort de 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 CLR (Common Language Runtime) .NET Framework lorsque le programme ou l'espace de noms contenant 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 des données d'instance et ne sont pas associées à un objet spécifique dans votre code. Vous pouvez utiliser une classe statique pour contenir 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 #).

Créer une classe statique revient donc fondamentalement à créer une classe qui ne contient que des membres statiques et un constructeur privé. Un constructeur privé empêche l'instanciation de la classe.

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 garantira 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 un constructeur d'instance; Cependant, ils peuvent avoir un constructeur statique. Pour plus d'informations, consultez Constructeurs statiques (Guide de programmation C #).

0
Zone