Perché non posso dichiarare i metodi C # virtuali e statici?

Ho una class helper che è solo un mucchio di metodi statici e vorrei sottoclassi la class helper. Alcuni comportamenti sono unici a seconda della sottoclass, quindi mi piacerebbe chiamare un metodo virtuale dalla class base, ma poiché tutti i metodi sono statici, non posso creare un semplice metodo virtuale (necessario riferimento all’object per accedere al metodo virtuale).

C’è un modo per aggirare questo? Credo che potrei usare un singleton .. HelperClass.Instance.HelperMethod () non è molto peggio di HelperClass.HelperMethod (). Punti Brownie per chiunque possa indicare alcune lingue che supportano metodi statici virtuali.

Modifica: OK sì, sono pazzo. I risultati di ricerca di Google mi hanno fatto pensare che non ero un po ‘lì.

I metodi statici virtuali non hanno senso. Se chiamo HelperClass.HelperMethod(); , perché dovrei aspettarmi che venga chiamato un metodo casuale di sottoclass? La soluzione si rompe davvero quando hai 2 sottoclassi di HelperClass – quale HelperClass ?

Se vuoi avere metodi sovrascrivibili di tipo statico dovresti probabilmente andare con:

  • Un singleton, se vuoi che la stessa sottoclass venga usata globalmente.
  • Una gerarchia di classi di tradizione, con un’iniezione di fabbrica o dipendenza, se si desidera un comportamento diverso in diverse parti dell’applicazione.

Scegli quale soluzione ha più senso nella tua situazione.

Non penso che tu sia pazzo. Vuoi solo usare ciò che è imansible attualmente in .NET.

La tua richiesta di metodo statico virtuale avrebbe molto senso se parliamo di generici. Ad esempio, la mia futura richiesta per i progettisti CLR è di permettermi di scrivere intereface in questo modo:

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

e usalo in questo modo:

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

Ma è imansible ora, quindi lo sto facendo in questo modo:

  public static class Static where T : new() { public static T Value = new T(); } public interface ISumable { T Add(T left, T right); } public T Aggregate(T left, T right) where T : ISumable, new() { return Static.Value.Add(left, right); } 

In effetti, questo può essere fatto in Delphi. Un esempio:

 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; 

Abbastanza interessante. Non uso più Delphi, ma ricordo di poter creare facilmente diversi tipi di controlli su una canvas di design personalizzata usando la funzione metaclass: la class di controllo, ad es. TButton, TTextBox ecc. Era un parametro e potevo chiamare il costruttore appropriato usando l’argomento del metaclass effettivo.

Tipo di modello di fabbrica del povero uomo 🙂

È ansible ottenere lo stesso effetto semplicemente con un metodo statico regolare e quindi ombreggiare con la new parola chiave

 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 { } 

Quindi puoi chiamare i metodi in questo modo

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

Vengo da Delfi e questa è una caratteristica tra le tante che mi manca molto nel c #. Delphi ti consentirebbe di creare riferimenti di tipo tipizzati e potresti passare il tipo di una class derivata ovunque fosse necessario il tipo di una class genitore. Questo trattamento di tipi come oggetti aveva una potente utilità. In particolare, consente la determinazione del tempo di esecuzione dei metadati. Sto mescolando orribilmente la syntax qui, ma in C # sembrerebbe qualcosa di simile:

  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); } } 

produrrebbe: radice derivata

esiste un metodo statico al di fuori di un’istanza di una class. Non può utilizzare dati non statici.

un metodo virtuale sarà “sovrascritto” da una funzione sovraccaricata che dipende dal tipo di un’istanza.

quindi hai una chiara contraddizione tra statico e virtuale.

Questo non è un problema di supporto, è un concetto.

Aggiornamento: mi è stato dimostrato sbagliato qui (vedi commenti):

Quindi dubito che troverai un linguaggio OOP che supporterà metodi statici virtuali.

Non sei pazzo. Ciò a cui ti riferisci si chiama Late Static Binding; è stato recentemente aggiunto a PHP. C’è un ottimo thread che lo descrive – qui: quando dovresti usare il binding statico avanzato?

Mart ha capito bene con la “nuova” parola chiave. Sono arrivato qui perché avevo bisogno di questo tipo di funzionalità e la soluzione di Mart funziona perfettamente. In effetti ne ho preso uno migliore e ho reso il mio abstract di metodo di class base per forzare il programmatore a fornire questo campo.

Il mio scenario era il seguente:

Ho una class base HouseDeed. Ogni tipo di casa è derivato da HouseDeed deve avere un prezzo.

Ecco la class HouseDeed base parziale:

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

Ora diamo un’occhiata a due tipi di case derivate:

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

e…

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

Come puoi vedere, posso accedere al prezzo della casa tramite il tipo SmallTowerDeed.m_price e l’istanza new SmallTowerDeed (). Prezzo In astratto, questo meccanismo costringe il programmatore a fornire un prezzo per ogni nuovo tipo di casa derivata.

Qualcuno ha sottolineato come “static virtual” e “virtual” siano concettualmente in disaccordo l’uno con l’altro. Non sono d’accordo. In questo esempio, i metodi statici non hanno bisogno di accedere ai dati di istanza, e quindi i requisiti che (1) il prezzo sia disponibile tramite il solo TYPE e che (2) un prezzo sia fornito.

Ho sentito che Delphi sostiene qualcosa del genere. Sembra che lo faccia rendendo le istanze di classi di un metaclass.

Non l’ho visto funzionare, quindi non sono sicuro che funzioni, o che senso ha.

PS Per favore correggimi se sbaglio, poiché non è il mio dominio.

Perché un metodo virtuale utilizza il tipo definito dell’object istanziato per determinare quale implementazione eseguire, (al contrario del tipo dichiarato della variabile di riferimento)

… e statico, ovviamente, è tutto senza preoccuparsi se c’è persino un’istanza istanziata della class …

Quindi questi sono incompatibili.

In conclusione, se si desidera modificare il comportamento in base alla sottoclass di un’istanza, i metodi dovrebbero essere metodi virtuali sulla class base, non metodi statici.

Tuttavia, poiché hai già questi metodi statici e ora devi sostituirli, puoi risolvere il tuo problema in questo modo: aggiungi i metodi di istanza virtuale alla class base che semplicemente delegano ai metodi statici e quindi esegui l’override di quei metodi di wrapper di istanze virtuali ( non quelli statici) in ogni sottoclass derivata, come appropriato …

In realtà è ansible combinare virtuale e statico per un metodo o un membro utilizzando la parola chiave new anziché virtual .

Ecco un esempio:

 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 

Ovviamente il valore TyreCount avrebbe potuto essere impostato nel metodo GetTyreCount override, ma ciò eviterebbe la duplicazione del valore. È ansible ottenere il valore sia dalla class che dall’istanza della class.

Ora qualcuno può trovare un utilizzo veramente intelligente di quella funzionalità?

Un metodo di sostituzione fornisce una nuova implementazione di un membro ereditato da una class base. Il metodo che viene sovrascritto da una dichiarazione di override è noto come metodo di base sottoposto a override. Il metodo di base sottoposto a override deve avere la stessa firma del metodo override. Non è ansible sovrascrivere un metodo non virtuale o statico. Il metodo di base sottoposto a override deve essere virtuale, astratto o override.

Una dichiarazione di override non può modificare l’accessibilità del metodo virtuale. Sia il metodo override che il metodo virtuale devono avere lo stesso modificatore del livello di accesso.

Non è ansible utilizzare i modificatori nuovi, statici o virtuali per modificare un metodo di sostituzione.

Una dichiarazione di proprietà sovrascritta deve specificare esattamente lo stesso modificatore di accesso, il tipo e il nome come proprietà ereditata e la proprietà sostituita deve essere virtuale, astratta o sovrascritta.

Esiste un modo per forzare un’eredità di metodi “astratti statici” da una class generica astratta. Vedi come segue:

 public abstract class Mother where T : Mother, new() { public abstract void DoSomething(); public static void Do() { (new T()).DoSomething(); } } public class ChildA : Mother { public override void DoSomething() { /* Your Code */ } } public class ChildB : Mother { public override void DoSomething() { /* Your Code */ } } 

Esempio (usando la madre precedente):

 public class ChildA : Mother { public override void DoSomething() { Console.WriteLine("42"); } } public class ChildB : Mother { public override void DoSomething() { Console.WriteLine("12"); } } public class Program { static void Main() { ChildA.Do(); //42 ChildB.Do(); //12 Console.ReadKey(); } } 

Non è così bello dal momento che puoi ereditare da una sola class astratta e ti chiederà di essere clemente con la tua nuova implementazione ().

Inoltre, penso che sarà costoso in termini di memoria a seconda delle dimensioni delle classi ereditate. In caso di problemi di memoria, è necessario impostare ogni proprietà / variabile dopo la nuova in un metodo pubblico che è un modo terribile per avere valori predefiniti.

Puoi usare la nuova parola chiave

 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); } } }