12 votes

Les méthodes statiques virtuelles (ou abstraites) en C#

L'héritage statique fonctionne exactement comme l'héritage d'instance. Sauf que vous n'êtes pas autorisé à rendre les méthodes statiques virtuelles ou abstraites.

class Program {
    static void Main(string[] args) {
        TestBase.TargetMethod();
        TestChild.TargetMethod();
        TestBase.Operation();
        TestChild.Operation();
    }
}

class TestBase {
    public static void TargetMethod() {
        Console.WriteLine("Classe de base");
    }

    public static void Operation() {
        TargetMethod();
    }
}

class TestChild : TestBase {
    public static new void TargetMethod() {
        Console.WriteLine("Classe enfant");
    }
}

Cela affichera :

Classe de base
Classe enfant
Classe de base
Classe de base

Mais je veux :

Classe de base
Classe enfant
Classe de base
Classe enfant

Si je pouvais sur les méthodes statiques, je rendrais TargetMethod virtuel et cela ferait l'affaire. Mais y a-t-il une solution de contournement pour obtenir le même effet ?

Modifier : Oui, je pourrais mettre une copie de Operation dans la classe enfant, mais cela nécessiterait de copier-coller un grand morceau de code dans chaque enfant, ce qui, dans mon cas, est d'environ 35 classes, un cauchemar à entretenir.

13voto

Stefan Steinegger Points 37073

Non, vous ne pouvez pas remplacer une méthode statique. "statique" signifie également qu'elle est liée statiquement par le compilateur, donc la méthode réelle à appeler n'est pas trouvée au moment de l'exécution, mais liée au moment de la compilation.

Ce que vous devriez faire est de rendre la classe non statique. Rendez la méthode virtuelle et remplacez-la pour tirer pleinement parti de l'héritage réel. Ensuite, si vous en avez vraiment besoin, créez un point d'entrée statique vers une référence de votre classe. Par exemple, une fabrique statique, un singleton (c'est un anti-pattern dans la plupart des cas mais est aussi bon qu'une classe statique) ou simplement une propriété statique.

9voto

Mark Brackett Points 46824

Vous pourriez stocker TargetMethod en tant que délégué, que une sous-classe pourrait changer en fonction de ses besoins :

class TestBase {
    protected static Action _targetMethod;

    static new() {
       _targetMethod = new Action(() => {
           Console.WriteLine("Classe de base");
       });
    }

    public static void TargetMethod() {
        _targetMethod();
    }

    public static void Operation() {
        TargetMethod();
    }
}

class TestChild : TestBase {
    static new() {
       _targetMethod = new Action(() => {
           Console.WriteLine("Classe enfant");
       });
    }
}

Cependant, étant donné qu'il s'agit d'instances statiques, le _targetMethod est partagé entre toutes les instances - le changer dans TestChild le modifie également pour TestBase. Cela peut vous importuner ou non. Si c'est le cas, les génériques ou un Dictionary pourraient être utiles.

En fin de compte, vous auriez probablement moins de difficultés si vous n'insistiez pas sur les membres statiques, ou peut-être utilisiez la composition au lieu de l'héritage.

2voto

Nick Whaley Points 1289

Si vous cherchez à faire des méthodes statiques abstraites, alors cela fonctionne, et finit par être la solution la plus facile pour moi de m'adapter à :

class TestBase where ChildType : TestBase {
    //public static abstract void TargetMethod();

    public static void Operation() {
        typeof(ChildType).GetMethod("TargetMethod").Invoke(null, null);
    }
}

class TestChild : TestBase {
    public static void TargetMethod() {
        Console.WriteLine("Classe enfant");
    }
}

Mais je marque toujours Stafan comme la solution parce que l'utilisation de l'héritage d'instance est probablement la meilleure recommandation pour quiconque dans une situation similaire. Mais je devrais simplement réécrire trop de code pour cela.

2voto

Jason D Points 46

D'accord, voici ce que j'ai fait

public abstract class Base
    where T : Base, new()
{
    #region Instance Singleton
    //Ceci est pour imiter l'implémentation statique des méthodes non spécifiques à l'instance
    private static object lockobj = new Object();
    private static T _Instance;
    public static T Instance
    {
        get
        {
            if (_Instance == null)
            {
                lock (lockobj)
                {
                    if (_Instance == null)
                    {
                        _Instance = new T();
                    }

                }
            }
            return _Instance;
        }
    }

    #endregion //Instance Singleton

    #region Définitions Abstract

    public abstract T GetByID(long id);
    public abstract T Fill(SqlDataReader sr);

    #endregion //Définitions Abstract
}

public class InstanceClass : Base
{
    //constructeur vide pour vous assurer d'obtenir seulement les définitions de méthode sans
    //que du code supplémentaire ne s'exécute
    public InstanceClass() { }

    #region Méthodes Base

    public override InstanceClass GetByID(long id)
    {
        SqlDataReader sr = DA.GetData("select * from table");
        return InstanceClass.Instance.Fill(sr);
    }

    internal override InstanceClass Fill(SqlDataReader sr)
    {
         InstanceClass returnVal = new InstanceClass();
         returnVal.property = sr["column1"];
         return returnVal;
    }
}

Je pense que cela sera une solution viable pour ce que vous voulez faire sans enfreindre trop de principes de purisme de la POO.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X