web-dev-qa-db-fra.com

Pourquoi ne puis-je pas déclarer les méthodes C # virtuelles et statiques?

J'ai une classe d'assistance qui n'est qu'un tas de méthodes statiques et je voudrais sous-classer la classe d'assistance. Certains comportements sont uniques en fonction de la sous-classe, donc je voudrais appeler une méthode virtuelle à partir de la classe de base, mais comme toutes les méthodes sont statiques, je ne peux pas créer une méthode virtuelle simple (j'ai besoin d'une référence d'objet pour accéder à la méthode virtuelle).

Y a-t-il un moyen de contourner cela? Je suppose que je pourrais utiliser un singleton .. HelperClass.Instance.HelperMethod () n'est pas tellement pire que HelperClass.HelperMethod (). Brownie pointe pour quiconque peut signaler certains langages qui prennent en charge les méthodes statiques virtuelles.

Edit: OK ouais je suis fou. Les résultats de recherche Google m'ont fait penser que je n'étais pas un peu là.

49
Luke

Les méthodes statiques virtuelles n'ont pas de sens. Si j'appelle HelperClass.HelperMethod();, pourquoi m'attendrais-je à ce qu'une méthode de sous-classe aléatoire soit appelée? La solution tombe vraiment en panne lorsque vous avez 2 sous-classes de HelperClass - laquelle utiliseriez-vous?

Si vous voulez avoir des méthodes de type statique remplaçables, vous devriez probablement aller avec:

  • Un singleton, si vous souhaitez que la même sous-classe soit utilisée globalement.
  • Une hiérarchie de classes de tradition, avec une injection de fabrique ou de dépendance, si vous souhaitez un comportement différent dans différentes parties de votre application.

Choisissez la solution la plus appropriée à votre situation.

42

Je ne pense pas que tu sois fou. Vous voulez simplement utiliser ce qui est actuellement impossible dans .NET.

Votre demande de méthode statique virtuelle aurait tellement de sens si nous parlons de génériques. Par exemple, ma future demande pour les concepteurs CLR est de me permettre d'écrire des interfaces comme ceci:

public interface ISumable<T>
{
  static T Add(T left, T right);
}

et utilisez-le comme ceci:

public T Aggregate<T>(T left, T right) where T : ISumable<T>
{
  return T.Add(left, right);
}

Mais c'est impossible en ce moment, alors je le fais comme ceci:

    public static class Static<T> where T : new()
    {
      public static T Value = new T();
    }

    public interface ISumable<T>
    {
      T Add(T left, T right);
    }

    public T Aggregate<T>(T left, T right) where T : ISumable<T>, new()
    {
      return Static<T>.Value.Add(left, right);
    }
32
SeeR

En effet, cela peut être fait dans Delphi. Un exemple:

type
  TForm1 = class(TForm)
    procedure FormShow(Sender: TObject);
  end;

  TTestClass = class
  public
    class procedure TestMethod(); virtual;
  end;

  TTestDerivedClass = class(TTestClass)
  public
    class procedure TestMethod(); override;
  end;

  TTestMetaClass = class of TTestClass;

var
  Form1: TForm1;

implementation

{$R *.dfm}

class procedure TTestClass.TestMethod();
begin
  Application.MessageBox('base', 'Message');
end;

class procedure TTestDerivedClass.TestMethod();
begin
  Application.MessageBox('descendant', 'Message');
end;


procedure TForm1.FormShow(Sender: TObject);
var
  sample: TTestMetaClass;
begin
  sample := TTestClass;
  sample.TestMethod;
  sample := TTestDerivedClass;
  sample.TestMethod;
end;

Plutôt interessant. Je n'utilise plus Delphi, mais je me souviens avoir été en mesure de créer très facilement différents types de contrôles sur un canevas de concepteur personnalisé en utilisant la fonction de métaclasse: la classe de contrôle, par exemple. TButton, TTextBox etc. était un paramètre, et je pouvais appeler le constructeur approprié en utilisant l'argument de la métaclasse réelle.

Une sorte de modèle d'usine du pauvre :)

15
Alan

Vous pouvez obtenir le même effet en ayant simplement une méthode statique régulière, puis en l'observant avec le mot clé new

public class Base 
{
    //Other stuff

    public static void DoSomething()
    {
        Console.WriteLine("Base");
    }
}

public class SomeClass : Base
{
    public new static void DoSomething()
    {
        Console.WriteLine("SomeClass");
    }
}
public class SomeOtherClass : Base
{
}

Ensuite, vous pouvez appeler les méthodes comme ça

Base.DoSomething(); //Base
SomeClass.DoSomething(); //SomeClass
SomeOtherClass.DoSomething(); //Base
12
Davy8

Je viens de Delphi et c'est une fonctionnalité parmi tant d'autres qui me manque cruellement en c #. Delphi vous permettrait de créer des références de type typées et vous pourriez passer le type d'une classe dérivée partout où le type d'une classe parente était nécessaire. Ce traitement des types en tant qu'objets avait une utilité puissante. En particulier, permettant la détermination du temps d'exécution des métadonnées. Je mélange horriblement la syntaxe ici mais en c # cela ressemblerait à quelque chose comme:

    class Root {
       public static virtual string TestMethod() {return "Root"; }
    }
    TRootClass = class of TRoot; // Here is the typed type declaration

    class Derived : Root {
       public static overide string TestMethod(){ return "derived"; }
    }

   class Test {
        public static string Run(){
           TRootClass rc;
           rc = Root;
           Test(rc);
           rc = Derived();
           Test(rc);
        }
        public static Test(TRootClass AClass){
           string str = AClass.TestMethod();
           Console.WriteLine(str);
        }
    } 

produirait: dérivé de racine

8
Ken Revak

Tu n'es pas fou. Ce à quoi vous faites référence est appelé liaison statique tardive; il a été récemment ajouté à PHP. Il y a un excellent fil qui le décrit - ici: Quand auriez-vous besoin d'utiliser une liaison statique tardive?

6
Alex Weinstein

Une méthode de remplacement fournit une nouvelle implémentation d'un membre hérité d'une classe de base. La méthode qui est remplacée par une déclaration de substitution est connue sous le nom de méthode de base substituée. La méthode de base substituée doit avoir la même signature que la méthode de substitution. Vous ne pouvez pas remplacer une méthode non virtuelle ou statique. La méthode de base substituée doit être virtuelle, abstraite ou remplacer.

Une déclaration de substitution ne peut pas modifier l'accessibilité de la méthode virtuelle. La méthode de remplacement et la méthode virtuelle doivent avoir le même modificateur de niveau d'accès.

Vous ne pouvez pas utiliser les nouveaux modificateurs statiques ou virtuels pour modifier une méthode de remplacement.

Une déclaration de propriété prioritaire doit spécifier exactement le même modificateur d'accès, type et nom que la propriété héritée, et la propriété substituée doit être virtuelle, abstraite ou remplacer.

1
user2761122

Il est en fait possible de combiner virtuel et statique pour une méthode ou un membre en utilisant le mot clé new au lieu de virtual.

Voici un exemple:

class Car
{
    public static int TyreCount = 4;
    public virtual int GetTyreCount() { return TyreCount; }
}
class Tricar : Car
{
    public static new int TyreCount = 3;
    public override int GetTyreCount() { return TyreCount; }
}

...

Car[] cc = new Car[] { new Tricar(), new Car() };
int t0 = cc[0].GetTyreCount(); // t0 == 3
int t1 = cc[1].GetTyreCount(); // t1 == 4

De toute évidence, la valeur TyreCount aurait pu être définie dans la méthode GetTyreCount substituée, mais cela évite de dupliquer la valeur. Il est possible d'obtenir la valeur à la fois de la classe et de l'instance de classe.

Maintenant, quelqu'un peut-il trouver une utilisation vraiment intelligente de cette fonctionnalité?

1
Mart

Parce qu'une méthode virtuelle utilise le type défini de l'objet instancié pour déterminer quelle implémentation exécuter, (par opposition au type déclaré de la variable de référence)

... et statique, bien sûr, ne sert à rien s'il y a même une instance instanciée de la classe ...

Ce sont donc incompatibles.

En résumé, si vous souhaitez modifier le comportement en fonction de la sous-classe d'une instance, les méthodes doivent avoir été des méthodes virtuelles sur la classe de base, et non des méthodes statiques.

Mais, comme vous disposez déjà de ces méthodes statiques et que vous devez maintenant les remplacer, vous pouvez résoudre votre problème de la manière suivante: ajoutez des méthodes d'instance virtuelle à la classe de base qui délèguent simplement aux méthodes statiques, puis remplacez ces méthodes d'encapsuleur d'instance virtuelle ( pas les statiques) dans chaque sous-classe dérivée, selon le cas ...

1
charles bretana

Il existe un moyen de forcer l'héritage des méthodes "statiques abstraites" d'une classe générique abstraite. Voir comme suit:

public abstract class Mother<T> where T : Mother<T>, new()
{
    public abstract void DoSomething();

    public static void Do()
    {
        (new T()).DoSomething();
    }

}

public class ChildA : Mother<ChildA>
{
    public override void DoSomething() { /* Your Code */ }
}

public class ChildB : Mother<ChildB>
{
    public override void DoSomething() { /* Your Code */ }
}

Exemple (en utilisant la mère précédente):

public class ChildA : Mother<ChildA>
{
    public override void DoSomething() { Console.WriteLine("42"); }
}

public class ChildB : Mother<ChildB>
{
    public override void DoSomething() { Console.WriteLine("12"); }
}

public class Program
{
    static void Main()
    {
        ChildA.Do();  //42
        ChildB.Do();  //12
        Console.ReadKey();
    }
}

Ce n'est pas génial car vous pouvez hériter d'une seule classe abstraite et il vous demandera d'être indulgent avec votre nouvelle implémentation ().

De plus, je pense que cela coûtera cher en mémoire en fonction de la taille de vos classes héritées. Dans le cas où vous avez un problème de mémoire, vous devrez définir toutes les propriétés/variables après votre nouveau dans une méthode publique, ce qui est une terrible façon d'avoir des valeurs par défaut.

1
Lostblue

J'ai entendu dire que Delphi supporte quelque chose comme ça. Il semble qu'il le fasse en faisant des classes des instances d'objets d'une métaclasse.

Je ne l'ai pas vu fonctionner, donc je ne suis pas sûr que cela fonctionne, ou quel est l'intérêt de cela.

P.S. Veuillez me corriger si je me trompe, car ce n'est pas mon domaine.

1
rslite

Mart a bien compris avec le "nouveau" mot clé. Je suis arrivé ici parce que j'avais besoin de ce type de fonctionnalité et la solution de Mart fonctionne très bien. En fait, je l'ai mieux pris et j'ai rendu abstraite ma méthode de classe de base pour forcer le programmeur à fournir ce champ.

Mon scénario était le suivant:

J'ai une classe de base HouseDeed. Chaque type de maison est dérivé de HouseDeed doit avoir un prix.

Voici la classe de base partielle HouseDeed:

public abstract class HouseDeed : Item
{
    public static int m_price = 0;
    public abstract int Price { get; }
    /* more impl here */
}

Voyons maintenant deux types de maison dérivés:

public class FieldStoneHouseDeed : HouseDeed
{
    public static new int m_price = 43800;
    public override int Price { get { return m_price; } }
    /* more impl here */
}

et...

public class SmallTowerDeed : HouseDeed
{
    public static new int m_price = 88500;
    public override int Price { get { return m_price; } }
    /* more impl here */
}

Comme vous pouvez le voir, je peux accéder au prix de la maison via le type SmallTowerDeed.m_price et l'instance new SmallTowerDeed (). Prix Et étant abstrait, ce mécanisme pousse le programmeur à fournir un prix pour chaque nouveau type de maison dérivée.

Quelqu'un a souligné à quel point "virtuel statique" et "virtuel" sont conceptuellement en contradiction l'un avec l'autre. Je ne suis pas d'accord. Dans cet exemple, les méthodes statiques n'ont pas besoin d'accéder aux données d'instance, et donc les exigences que (1) le prix soit disponible via le TYPE seul, et (2) qu'un prix soit fourni sont remplies.

1
luket

Vous pouvez utiliser le nouveau mot-clé

namespace AspDotNetStorefront
{
    // This Class is need to override StudioOnlineCommonHelper Methods in a branch
    public class StudioOnlineCommonHelper : StudioOnlineCore.StudioOnlineCommonHelper
    {
        //
        public static new void DoBusinessRulesChecks(Page page)
        {
            StudioOnlineCore.StudioOnlineCommonHelper.DoBusinessRulesChecks(page);
        }
    }
}
0
Dennis Walter