web-dev-qa-db-fra.com

C #, implémente les méthodes 'abstraites statiques'

J'ai récemment rencontré un problème où il me semble avoir besoin d'une méthode 'abstraite statique'. Je sais pourquoi c'est impossible, mais comment puis-je contourner cette limitation?

Par exemple, j'ai une classe abstraite qui a une chaîne de description. Étant donné que cette chaîne est commune à toutes les instances, elle est marquée comme statique, mais je veux exiger que toutes les classes dérivées de cette classe fournissent leur propre propriété Description.

abstract class AbstractBase
{
    ...
    public static abstract string Description{get;}
    ...
}

Ça ne compilera pas, bien sûr. J'ai pensé utiliser des interfaces, mais les interfaces ne peuvent pas contenir de signatures de méthodes statiques.

Devrais-je le rendre simplement non statique et toujours demander à une instance d'obtenir les informations spécifiques à cette classe?

Des idées?

38
Calmarius

Combiner statique et abstrait n'a pas beaucoup de sens, oui. L'idée derrière static est qu'il n'est pas nécessaire de présenter une instance de la classe pour utiliser le membre en question; Cependant, avec abstract, on s'attend à ce qu'une instance soit une classe dérivée fournissant une implémentation concrète.

Je peux voir pourquoi vous voudriez ce genre de combinaison, mais le fait est que le seul effet serait de refuser l'utilisation de "this" ou de tout membre non statique à la mise en oeuvre. C'est-à-dire que la classe mère dicterait une restriction dans l'implémentation de la classe dérivée, même s'il n'y a aucune différence sous-jacente entre l'appel d'un membre abstrait ou 'membre abstrait statique' (dans la mesure où les deux auraient besoin d'une instance concrète pour déterminer quelle implémentation utiliser)

5
bdonlan

Vous ne pouvez pas.

L'endroit pour faire ceci est avec les attributs.

Par exemple

[Name("FooClass")]
class Foo
{
}
31
leppie

Si cela ne vous dérange pas de passer aux implémentations pour implémenter judicieusement la propriété Description, vous pouvez simplement le faire.

public abstract string ClassDescription {get; } 
// ClassDescription is more intention-revealing than Description

Et implémenter des classes ferait quelque chose comme ceci:

static string classDescription="My Description for this class";
override string  ClassDescription { get { return classDescription; } }

Ensuite, vos classes sont tenues de respecter le contrat de description, mais vous leur laissez le soin de le faire de manière sensée. Il n'y a aucun moyen de spécifier une implémentation de manière orientée objet (sauf par des piratages cruels et fragiles).

Cependant, dans mon esprit, cette description est une métadonnée de classe, je préférerais donc utiliser le mécanisme d'attribut comme d'autres l'ont décrit. Si vous êtes particulièrement préoccupé par les utilisations multiples de la réflexion, créez un objet qui reflète l'attribut qui vous intéresse et stockez un dictionnaire entre le type et la description. Cela minimisera la réflexion (autre que l'inspection de type à l'exécution, qui n'est pas si mauvaise). Le dictionnaire peut être stocké en tant que membre de la classe qui a généralement besoin de ces informations ou, si les clients du domaine en ont besoin, via un singleton ou un objet de contexte.

6
JasonTrue

Si c'est statique, il n'y a qu'une instance de la variable, je ne vois pas en quoi un héritage aurait un sens si nous pouvions faire ce que vous voulez accomplir avec des vars statiques dans des classes dérivées. Personnellement, je pense que vous allez trop loin pour essayer d'éviter une instance var.

Pourquoi ne pas simplement la manière classique? 

abstract class AbstractBase
{
    protected string _Description = "I am boring abstract default value";
}

class Foo : AbstractBase {

     public Foo() {
       _Description = "I am foo!";
     }
}
5
Chad Grant

Ce n'est pas statique s'il doit être appelé sur une instance.

Si vous ne l'appelez pas sur une instance, aucun polymorphisme n'est en jeu (par exemple, ChildA.Description n'a aucun lien avec ChildB.Description en ce qui concerne le langage).

3
Yuliy

Une solution possible consiste à définir un singleton de votre classe dérivée dans votre classe de base à l'aide de Generics.

import System;

public abstract class AbstractBase<T>
    where T : AbstractBase<T>, new()
{
    private static T _instance = new T();

    public abstract string Description { get; }
    public static string GetDescription()
    {
        return _instance.Description;
    }
}

public class DerivedClass : AbstractBase<DerivedClass>
{
    public override string Description => "This is the derived Class";
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(DerivedClass.GetDescription());
        Console.ReadKey();
    }
}

L'astuce consiste à donner à votre AbstractBase<T> quelques détails sur la manière dont DerivedClass est implémenté:

  • Il est newable avec where T: new() pour pouvoir créer une instance Singleton
  • Il dérive de lui-même avec where T : AbstractBase<T> afin qu'il sache qu'il y aura une implémentation de Description

De cette façon, _instance contient le champ Description qui peut être appelé dans la méthode statique GetDescription(). Ceci vous oblige à écraser Descriptiondans votre DerivedClass et vous permet d'appeler sa valeur avec DerivedClass.GetDescription()

1
Manuel Gschrey

Vous pouvez faire en sorte que la méthode de base "abstraite" jette une Exception. Ainsi, un développeur est "averti" s'il tente d'appeler cette méthode sur une classe enfant sans le surcharger. 

L'inconvénient est que l'on peut étendre la classe sans utiliser cette méthode. Ensuite, reportez-vous aux autres réponses fournies.

0
Wojciech